diff --git a/HISTORY.md b/HISTORY.md index f100edceb..daa16f7a1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +# v0.8.1 + +* [FIXED] Mysql error "LIMIT in subquery" [#77](https://github.com/doug-martin/nestjs-query/issues/77) + * Changed `nestjs-query/query-typeorm` to not use subqueries to fetch relations. + # v0.8.0 * [FIXED] Defining additional UpdateDtos breaks Schema. [#72](https://github.com/doug-martin/nestjs-query/issues/72) diff --git a/package-lock.json b/package-lock.json index 95e893623..19d0eb15c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4193,14 +4193,6 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" }, - "@types/lodash.filter": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/@types/lodash.filter/-/lodash.filter-4.6.6.tgz", - "integrity": "sha512-K9oEglaInmu7pnQnZYdciNePpKe0W7O9yssnCza9mLjpq5N5Ju8RIMwTvp9tovb8V5yemxFTJqHzG+tIkAl1xw==", - "requires": { - "@types/lodash": "*" - } - }, "@types/lodash.omit": { "version": "4.5.6", "resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.6.tgz", @@ -13405,11 +13397,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -18308,22 +18295,26 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "optional": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "optional": true, "requires": { "delegates": "^1.0.0", @@ -18332,12 +18323,14 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "optional": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "optional": true, "requires": { "balanced-match": "^1.0.0", @@ -18346,32 +18339,38 @@ }, "chownr": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "optional": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, "debug": { "version": "3.2.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "optional": true, "requires": { "ms": "^2.1.1" @@ -18379,22 +18378,26 @@ }, "deep-extend": { "version": "0.6.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "optional": true }, "fs-minipass": { "version": "1.2.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "optional": true, "requires": { "minipass": "^2.6.0" @@ -18402,12 +18405,14 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "optional": true, "requires": { "aproba": "^1.0.3", @@ -18422,7 +18427,8 @@ }, "glob": { "version": "7.1.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "optional": true, "requires": { "fs.realpath": "^1.0.0", @@ -18435,12 +18441,14 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, "iconv-lite": { "version": "0.4.24", - "bundled": true, + "resolved": false, + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -18448,7 +18456,8 @@ }, "ignore-walk": { "version": "3.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "optional": true, "requires": { "minimatch": "^3.0.4" @@ -18456,7 +18465,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "optional": true, "requires": { "once": "^1.3.0", @@ -18465,17 +18475,20 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "optional": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "optional": true, "requires": { "number-is-nan": "^1.0.0" @@ -18483,12 +18496,14 @@ }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "optional": true, "requires": { "brace-expansion": "^1.1.7" @@ -18496,12 +18511,14 @@ }, "minimist": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "optional": true }, "minipass": { "version": "2.9.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "optional": true, "requires": { "safe-buffer": "^5.1.2", @@ -18510,7 +18527,8 @@ }, "minizlib": { "version": "1.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "optional": true, "requires": { "minipass": "^2.9.0" @@ -18518,7 +18536,8 @@ }, "mkdirp": { "version": "0.5.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "optional": true, "requires": { "minimist": "^1.2.5" @@ -18526,12 +18545,14 @@ }, "ms": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "optional": true }, "needle": { "version": "2.3.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", "optional": true, "requires": { "debug": "^3.2.6", @@ -18541,7 +18562,8 @@ }, "node-pre-gyp": { "version": "0.14.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "optional": true, "requires": { "detect-libc": "^1.0.2", @@ -18558,7 +18580,8 @@ }, "nopt": { "version": "4.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "optional": true, "requires": { "abbrev": "1", @@ -18567,7 +18590,8 @@ }, "npm-bundled": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "optional": true, "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -18575,12 +18599,14 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "optional": true }, "npm-packlist": { "version": "1.4.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "optional": true, "requires": { "ignore-walk": "^3.0.1", @@ -18590,7 +18616,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "optional": true, "requires": { "are-we-there-yet": "~1.1.2", @@ -18601,17 +18628,20 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "optional": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, "once": { "version": "1.4.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "optional": true, "requires": { "wrappy": "1" @@ -18619,17 +18649,20 @@ }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "optional": true, "requires": { "os-homedir": "^1.0.0", @@ -18638,17 +18671,20 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "optional": true }, "process-nextick-args": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "optional": true }, "rc": { "version": "1.2.8", - "bundled": true, + "resolved": false, + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "optional": true, "requires": { "deep-extend": "^0.6.0", @@ -18659,7 +18695,8 @@ }, "readable-stream": { "version": "2.3.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "optional": true, "requires": { "core-util-is": "~1.0.0", @@ -18673,7 +18710,8 @@ }, "rimraf": { "version": "2.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "optional": true, "requires": { "glob": "^7.1.3" @@ -18681,37 +18719,44 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "optional": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "optional": true }, "semver": { "version": "5.7.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "optional": true, "requires": { "code-point-at": "^1.0.0", @@ -18721,7 +18766,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "optional": true, "requires": { "safe-buffer": "~5.1.0" @@ -18729,7 +18775,8 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "optional": true, "requires": { "ansi-regex": "^2.0.0" @@ -18737,12 +18784,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "optional": true }, "tar": { "version": "4.4.13", - "bundled": true, + "resolved": false, + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "optional": true, "requires": { "chownr": "^1.1.1", @@ -18756,12 +18805,14 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "optional": true }, "wide-align": { "version": "1.1.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "optional": true, "requires": { "string-width": "^1.0.2 || 2" @@ -18769,12 +18820,14 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "optional": true } } diff --git a/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts b/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts index 22d94645c..0223a3d9f 100644 --- a/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/relation-query.builder.spec.ts @@ -9,127 +9,73 @@ describe('RelationQueryBuilder', (): void => { beforeEach(createTestConnection); afterEach(closeTestConnection); - const baseManyToOneFrom = - ` FROM "test_entity" "testEntity"` + - ` INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."test_entity_id" = "testEntity"."testEntityPk"`; - - const manyToOneEntityClause = '"TestRelation"."testRelationPk" = ?'; - - const baseManyToOneFromSubQuery = `"testEntity"."testEntityPk" IN (SELECT "testEntity"."testEntityPk" AS "testEntity_testEntityPk"${baseManyToOneFrom} WHERE ${manyToOneEntityClause})`; - - const manyToOneOrderBy = `ORDER BY "TestRelation"."testRelationPk" ASC`; - - const orderByTestRelationsTestEntityId = `ORDER BY "testRelations"."test_entity_id" ASC`; - - const baseManyToOneSelect = `${ + const manyToOneSelect = `SELECT` + ` "testEntity"."testEntityPk" AS "testEntity_testEntityPk",` + ` "testEntity"."string_type" AS "testEntity_string_type",` + ` "testEntity"."bool_type" AS "testEntity_bool_type",` + ` "testEntity"."number_type" AS "testEntity_number_type",` + ` "testEntity"."date_type" AS "testEntity_date_type",` + - ` "testEntity"."oneTestRelationTestRelationPk" AS "testEntity_oneTestRelationTestRelationPk",` + - ` "TestRelation"."testRelationPk" AS "__nestjsQueryEntityId_testRelationPk__"` - }${baseManyToOneFrom}`; - - const baseManyToManyNonOwnerSelectQueryFrom = - ` FROM "test_entity" "manyTestEntities"` + - ` INNER JOIN "test_entity_many_test_relations_test_relation" "test_entity_many_test_relations_test_relation" ON "test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = "manyTestEntities"."testEntityPk"`; + ` "testEntity"."oneTestRelationTestRelationPk" AS "testEntity_oneTestRelationTestRelationPk"` + + ` FROM "test_entity" "testEntity"` + + ` INNER JOIN "test_relation" "TestRelation" ON "TestRelation"."test_entity_id" = "testEntity"."testEntityPk"` + + ` WHERE ("TestRelation"."testRelationPk" = ?)`; - const baseManyToManyNonOwnerSelectQuery = `${ + const manyToManyNonOwnerSelectQuery = `SELECT` + ` "manyTestEntities"."testEntityPk" AS "manyTestEntities_testEntityPk",` + ` "manyTestEntities"."string_type" AS "manyTestEntities_string_type",` + ` "manyTestEntities"."bool_type" AS "manyTestEntities_bool_type",` + ` "manyTestEntities"."number_type" AS "manyTestEntities_number_type",` + ` "manyTestEntities"."date_type" AS "manyTestEntities_date_type",` + - ` "manyTestEntities"."oneTestRelationTestRelationPk" AS "manyTestEntities_oneTestRelationTestRelationPk",` + - ` "test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" AS "__nestjsQueryEntityId_testRelationPk__"` - }${baseManyToManyNonOwnerSelectQueryFrom}`; - - const manyToManyNonOwnerEntityClause = - '"test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" = ?'; - - const manyToManyNonOwnerInSubQuery = `"manyTestEntities"."testEntityPk" IN (SELECT "manyTestEntities"."testEntityPk" AS "manyTestEntities_testEntityPk"${baseManyToManyNonOwnerSelectQueryFrom} WHERE ${manyToManyNonOwnerEntityClause})`; - - const manyToManyOrderBy = `ORDER BY "test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" ASC`; + ` "manyTestEntities"."oneTestRelationTestRelationPk" AS "manyTestEntities_oneTestRelationTestRelationPk"` + + ` FROM "test_entity" "manyTestEntities"` + + ` INNER JOIN "test_entity_many_test_relations_test_relation" "test_entity_many_test_relations_test_relation" ON "test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = "manyTestEntities"."testEntityPk"` + + ' WHERE ("test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" = ?)'; - const baseOneToManySelect = + const oneToManySelect = `SELECT` + ` "testRelations"."testRelationPk" AS "testRelations_testRelationPk",` + ` "testRelations"."relation_name" AS "testRelations_relation_name",` + - ` "testRelations"."test_entity_id" AS "testRelations_test_entity_id",` + - ` "testRelations"."test_entity_id" AS "__nestjsQueryEntityId_testEntityPk__"` + - ` FROM "test_relation" "testRelations"`; - - const oneToManyEntityClause = `"testRelations"."test_entity_id" = ?`; - - const baseOneToManySubQuery = `"testRelations"."testRelationPk" IN (SELECT "testRelations"."testRelationPk" AS "testRelations_testRelationPk" FROM "test_relation" "testRelations" WHERE ${oneToManyEntityClause})`; - - const baseManyToManyOwnerFrom = - ` FROM "test_relation" "manyTestRelations"` + - ` INNER JOIN "test_entity_many_test_relations_test_relation" "test_entity_many_test_relations_test_relation" ON "test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" = "manyTestRelations"."testRelationPk"`; + ` "testRelations"."test_entity_id" AS "testRelations_test_entity_id"` + + ` FROM "test_relation" "testRelations"` + + ' WHERE ("testRelations"."test_entity_id" = ?)'; - const baseManyToManyOwnerSelect = `${ + const manyToManyOwnerSelect = 'SELECT ' + `"manyTestRelations"."testRelationPk" AS "manyTestRelations_testRelationPk",` + ' "manyTestRelations"."relation_name" AS "manyTestRelations_relation_name",' + - ' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id",' + - ' "test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" AS "__nestjsQueryEntityId_testEntityPk__"' - }${baseManyToManyOwnerFrom}`; - - const manyToManyOwnerEntityClause = '"test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = ?'; - - const manyToManyOwnerSubQuery = `"manyTestRelations"."testRelationPk" IN (SELECT "manyTestRelations"."testRelationPk" AS "manyTestRelations_testRelationPk"${baseManyToManyOwnerFrom} WHERE ${manyToManyOwnerEntityClause})`; - - const baseOneToOneOwnerFrom = - ` FROM "test_relation" "oneTestRelation"` + - ` INNER JOIN "test_entity" "TestEntity" ON "TestEntity"."oneTestRelationTestRelationPk" = "oneTestRelation"."testRelationPk"`; - - const oneToOneOwnerEntityClause = '"TestEntity"."testEntityPk" = ?'; - const baseOneToOneOwnerSubQuery = `"oneTestRelation"."testRelationPk" IN (SELECT "oneTestRelation"."testRelationPk" AS "oneTestRelation_testRelationPk"${baseOneToOneOwnerFrom} WHERE ${oneToOneOwnerEntityClause})`; - - const oneToOneOwnerOrderBy = `ORDER BY "TestEntity"."testEntityPk" ASC`; + ' "manyTestRelations"."test_entity_id" AS "manyTestRelations_test_entity_id"' + + ` FROM "test_relation" "manyTestRelations"` + + ` INNER JOIN "test_entity_many_test_relations_test_relation" "test_entity_many_test_relations_test_relation" ON "test_entity_many_test_relations_test_relation"."testRelationTestRelationPk" = "manyTestRelations"."testRelationPk"` + + ' WHERE ("test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" = ?)'; - const baseOneToOneOwnerSelect = + const oneToOneOwnerSelect = `SELECT` + ` "oneTestRelation"."testRelationPk" AS "oneTestRelation_testRelationPk",` + ` "oneTestRelation"."relation_name" AS "oneTestRelation_relation_name",` + - ` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id",` + - ` "TestEntity"."testEntityPk" AS "__nestjsQueryEntityId_testEntityPk__"` + - `${baseOneToOneOwnerFrom}`; - - const baseOneToOneNonOwnerFrom = ` FROM "test_entity" "oneTestEntity"`; - const oneToOneNonOwnerEntityClause = '"oneTestEntity"."oneTestRelationTestRelationPk" = ?'; - const baseOneToOneNonOwnerSubQuery = `"oneTestEntity"."testEntityPk" IN (SELECT "oneTestEntity"."testEntityPk" AS "oneTestEntity_testEntityPk"${baseOneToOneNonOwnerFrom} WHERE ${oneToOneNonOwnerEntityClause})`; - - const manyToOneNonOwnerOrderBy = `ORDER BY "oneTestEntity"."oneTestRelationTestRelationPk" ASC`; + ` "oneTestRelation"."test_entity_id" AS "oneTestRelation_test_entity_id"` + + ` FROM "test_relation" "oneTestRelation"` + + ` INNER JOIN "test_entity" "TestEntity" ON "TestEntity"."oneTestRelationTestRelationPk" = "oneTestRelation"."testRelationPk"` + + ' WHERE ("TestEntity"."testEntityPk" = ?)'; - const baseOneToOneNonOwnerSelect = `${ + const oneToOneNonOwnerSelect = `SELECT` + ` "oneTestEntity"."testEntityPk" AS "oneTestEntity_testEntityPk",` + ` "oneTestEntity"."string_type" AS "oneTestEntity_string_type",` + ` "oneTestEntity"."bool_type" AS "oneTestEntity_bool_type",` + ` "oneTestEntity"."number_type" AS "oneTestEntity_number_type",` + ` "oneTestEntity"."date_type" AS "oneTestEntity_date_type",` + - ` "oneTestEntity"."oneTestRelationTestRelationPk" AS "oneTestEntity_oneTestRelationTestRelationPk",` + - ` "oneTestEntity"."oneTestRelationTestRelationPk" AS "__nestjsQueryEntityId_testRelationPk__"` - }${baseOneToOneNonOwnerFrom}`; - - const baseManyToManyCustomJoinFrom = ` FROM "test_entity_relation_entity" "testEntityRelation"`; - - const baseManyToManyCustomJoinEntityClause = `"testEntityRelation"."test_entity_id" = ?`; - - const baseManyToManyCustomJoinSubQuery = `("testEntityRelation"."test_relation_id", "testEntityRelation"."test_entity_id") IN (SELECT "testEntityRelation"."test_relation_id" AS "testEntityRelation_test_relation_id", "testEntityRelation"."test_entity_id" AS "testEntityRelation_test_entity_id"${baseManyToManyCustomJoinFrom} WHERE ${baseManyToManyCustomJoinEntityClause})`; - - const manyToManyCustomJoinOrderBy = `ORDER BY "testEntityRelation"."test_entity_id" ASC`; + ` "oneTestEntity"."oneTestRelationTestRelationPk" AS "oneTestEntity_oneTestRelationTestRelationPk"` + + ` FROM "test_entity" "oneTestEntity"` + + ' WHERE ("oneTestEntity"."oneTestRelationTestRelationPk" = ?)'; - const baseManyToManyCustomJoinSelect = `${ + const manyToManyCustomJoinSelect = `SELECT ` + `"testEntityRelation"."test_relation_id" AS "testEntityRelation_test_relation_id",` + - ` "testEntityRelation"."test_entity_id" AS "testEntityRelation_test_entity_id",` + - ` "testEntityRelation"."test_entity_id" AS "__nestjsQueryEntityId_testEntityPk__"` - }${baseManyToManyCustomJoinFrom}`; + ` "testEntityRelation"."test_entity_id" AS "testEntityRelation_test_entity_id"` + + ` FROM "test_entity_relation_entity" "testEntityRelation"` + + ` WHERE ("testEntityRelation"."test_entity_id" = ?)`; const getRelationQueryBuilder = ( EntityClass: Class, @@ -144,7 +90,7 @@ describe('RelationQueryBuilder', (): void => { }; const createSQLAsserter = (EntityClass: Class, baseSQL: string) => ( - entity: Entity | Entity[], + entity: Entity, relation: string, query: Query, expectedSql: string, @@ -154,19 +100,19 @@ describe('RelationQueryBuilder', (): void => { assertSQL(selectQueryBuilder, `${baseSQL}${expectedSql}`, expectedArgs); }; - const assertOneToManySQL = createSQLAsserter(TestEntity, baseOneToManySelect); + const assertOneToManySQL = createSQLAsserter(TestEntity, oneToManySelect); - const assertManyToManyOwnerSQL = createSQLAsserter(TestEntity, baseManyToManyOwnerSelect); + const assertManyToManyOwnerSQL = createSQLAsserter(TestEntity, manyToManyOwnerSelect); - const assertManyToOneSQL = createSQLAsserter(TestRelation, baseManyToOneSelect); + const assertManyToOneSQL = createSQLAsserter(TestRelation, manyToOneSelect); - const assertManyToManyNonOwnerSQL = createSQLAsserter(TestRelation, baseManyToManyNonOwnerSelectQuery); + const assertManyToManyNonOwnerSQL = createSQLAsserter(TestRelation, manyToManyNonOwnerSelectQuery); - const assertOneToOneOwnerSQL = createSQLAsserter(TestEntity, baseOneToOneOwnerSelect); + const assertOneToOneOwnerSQL = createSQLAsserter(TestEntity, oneToOneOwnerSelect); - const assertOneToOneNonOwnerSQL = createSQLAsserter(TestRelation, baseOneToOneNonOwnerSelect); + const assertOneToOneNonOwnerSQL = createSQLAsserter(TestRelation, oneToOneNonOwnerSelect); - const assertManyToManyCustomJoinSQL = createSQLAsserter(TestEntity, baseManyToManyCustomJoinSelect); + const assertManyToManyCustomJoinSQL = createSQLAsserter(TestEntity, manyToManyCustomJoinSelect); describe('#select', () => { const testEntity: TestEntity = { @@ -190,178 +136,57 @@ describe('RelationQueryBuilder', (): void => { describe('one to many', () => { it('should query with a single entity', () => { - assertOneToManySQL( - testEntity, - 'testRelations', - {}, - ` WHERE ((${oneToManyEntityClause})) AND ((${baseOneToManySubQuery})) ORDER BY "testRelations"."test_entity_id" ASC`, - [testEntity.testEntityPk, testEntity.testEntityPk], - ); - }); - - it('should work with multiple entities', () => { - assertOneToManySQL( - [testEntity, { ...testEntity, testEntityPk: 'id-2' }, { ...testEntity, testEntityPk: 'id-3' }], - 'testRelations', - {}, - ` WHERE ((${oneToManyEntityClause}) OR (${oneToManyEntityClause}) OR (${oneToManyEntityClause})) AND ((${baseOneToManySubQuery}) OR (${baseOneToManySubQuery}) OR (${baseOneToManySubQuery})) ORDER BY "testRelations"."test_entity_id" ASC`, - [testEntity.testEntityPk, 'id-2', 'id-3', testEntity.testEntityPk, 'id-2', 'id-3'], - ); + assertOneToManySQL(testEntity, 'testRelations', {}, ``, [testEntity.testEntityPk]); }); }); describe('many to one', () => { it('should work with one entity', () => { - assertManyToOneSQL( - testRelation, - 'testEntity', - {}, - ` WHERE ((${manyToOneEntityClause})) AND ((${baseManyToOneFromSubQuery})) ${manyToOneOrderBy}`, - [testRelation.testRelationPk, testRelation.testRelationPk], - ); - }); - - it('should work with multiple entities', () => { - assertManyToOneSQL( - [testRelation, { ...testRelation, testRelationPk: 'id-2' }, { ...testRelation, testRelationPk: 'id-3' }], - 'testEntity', - {}, - ` WHERE ((${manyToOneEntityClause}) OR (${manyToOneEntityClause}) OR (${manyToOneEntityClause})) AND ((${baseManyToOneFromSubQuery}) OR (${baseManyToOneFromSubQuery}) OR (${baseManyToOneFromSubQuery})) ${manyToOneOrderBy}`, - [testRelation.testRelationPk, 'id-2', 'id-3', testRelation.testRelationPk, 'id-2', 'id-3'], - ); + assertManyToOneSQL(testRelation, 'testEntity', {}, ``, [testRelation.testRelationPk]); }); }); describe('many to many', () => { describe('on owning side', () => { it('should work with one entity', () => { - assertManyToManyOwnerSQL( - testEntity, - 'manyTestRelations', - {}, - ` WHERE ((${manyToManyOwnerEntityClause})) AND ((${manyToManyOwnerSubQuery})) ORDER BY "test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" ASC`, - [testEntity.testEntityPk, testEntity.testEntityPk], - ); - }); - - it('should work with mutliple entities', () => { - assertManyToManyOwnerSQL( - [testEntity, { ...testEntity, testEntityPk: 'id-2' }, { ...testEntity, testEntityPk: 'id-3' }], - 'manyTestRelations', - {}, - ` WHERE ((${manyToManyOwnerEntityClause}) OR (${manyToManyOwnerEntityClause}) OR (${manyToManyOwnerEntityClause})) AND ((${manyToManyOwnerSubQuery}) OR (${manyToManyOwnerSubQuery}) OR (${manyToManyOwnerSubQuery})) ORDER BY "test_entity_many_test_relations_test_relation"."testEntityTestEntityPk" ASC`, - [testEntity.testEntityPk, 'id-2', 'id-3', testEntity.testEntityPk, 'id-2', 'id-3'], - ); + assertManyToManyOwnerSQL(testEntity, 'manyTestRelations', {}, ``, [testEntity.testEntityPk]); }); }); describe('on non owning side', () => { it('should work with many to many', () => { - assertManyToManyNonOwnerSQL( - testRelation, - 'manyTestEntities', - {}, - ` WHERE ((${manyToManyNonOwnerEntityClause})) AND ((${manyToManyNonOwnerInSubQuery})) ${manyToManyOrderBy}`, - [testRelation.testRelationPk, testRelation.testRelationPk], - ); - }); - - it('should work with many to many with multiple entities', () => { - assertManyToManyNonOwnerSQL( - [testRelation, { ...testRelation, testRelationPk: 'id-2' }, { ...testRelation, testRelationPk: 'id-3' }], - 'manyTestEntities', - {}, - ` WHERE ((${manyToManyNonOwnerEntityClause}) OR (${manyToManyNonOwnerEntityClause}) OR (${manyToManyNonOwnerEntityClause})) AND ((${manyToManyNonOwnerInSubQuery}) OR (${manyToManyNonOwnerInSubQuery}) OR (${manyToManyNonOwnerInSubQuery})) ${manyToManyOrderBy}`, - [testRelation.testRelationPk, 'id-2', 'id-3', testRelation.testRelationPk, 'id-2', 'id-3'], - ); + assertManyToManyNonOwnerSQL(testRelation, 'manyTestEntities', {}, ``, [testRelation.testRelationPk]); }); }); describe('many-to-many custom join table', () => { it('should work with a many-to-many through a join table', () => { - assertManyToManyCustomJoinSQL( - testEntity, - 'testEntityRelation', - {}, - ` WHERE ((${baseManyToManyCustomJoinEntityClause})) AND ((${baseManyToManyCustomJoinSubQuery})) ${manyToManyCustomJoinOrderBy}`, - [testEntity.testEntityPk, testEntity.testEntityPk], - ); + assertManyToManyCustomJoinSQL(testEntity, 'testEntityRelation', {}, ``, [testEntity.testEntityPk]); }); }); }); describe('one to one', () => { - describe('on owning side', () => { - it('should work with one entity', () => { - assertOneToOneOwnerSQL( - testEntity, - 'oneTestRelation', - {}, - ` WHERE ((${oneToOneOwnerEntityClause})) AND ((${baseOneToOneOwnerSubQuery})) ${oneToOneOwnerOrderBy}`, - [testEntity.testEntityPk, testEntity.testEntityPk], - ); - }); - - it('should work with mutliple entities', () => { - assertOneToOneOwnerSQL( - [testEntity, { ...testEntity, testEntityPk: 'id-2' }, { ...testEntity, testEntityPk: 'id-3' }], - 'oneTestRelation', - {}, - ` WHERE ((${oneToOneOwnerEntityClause}) OR (${oneToOneOwnerEntityClause}) OR (${oneToOneOwnerEntityClause})) AND ((${baseOneToOneOwnerSubQuery}) OR (${baseOneToOneOwnerSubQuery}) OR (${baseOneToOneOwnerSubQuery})) ${oneToOneOwnerOrderBy}`, - [testEntity.testEntityPk, 'id-2', 'id-3', testEntity.testEntityPk, 'id-2', 'id-3'], - ); - }); + it('on owning side', () => { + assertOneToOneOwnerSQL(testEntity, 'oneTestRelation', {}, ``, [testEntity.testEntityPk]); }); - describe('on non owning side', () => { - it('should work with many to many', () => { - assertOneToOneNonOwnerSQL( - testRelation, - 'oneTestEntity', - {}, - ` WHERE ((${oneToOneNonOwnerEntityClause})) AND ((${baseOneToOneNonOwnerSubQuery})) ${manyToOneNonOwnerOrderBy}`, - [testRelation.testRelationPk, testRelation.testRelationPk], - ); - }); - - it('should work with many to many with multiple entities', () => { - assertOneToOneNonOwnerSQL( - [testRelation, { ...testRelation, testRelationPk: 'id-2' }, { ...testRelation, testRelationPk: 'id-3' }], - 'oneTestEntity', - {}, - ` WHERE ((${oneToOneNonOwnerEntityClause}) OR (${oneToOneNonOwnerEntityClause}) OR (${oneToOneNonOwnerEntityClause})) AND ((${baseOneToOneNonOwnerSubQuery}) OR (${baseOneToOneNonOwnerSubQuery}) OR (${baseOneToOneNonOwnerSubQuery})) ${manyToOneNonOwnerOrderBy}`, - [testRelation.testRelationPk, 'id-2', 'id-3', testRelation.testRelationPk, 'id-2', 'id-3'], - ); - }); + it('on non owning side', () => { + assertOneToOneNonOwnerSQL(testRelation, 'oneTestEntity', {}, ``, [testRelation.testRelationPk]); }); }); describe('with filter', () => { it('should call whereBuilder#build if there is a filter', () => { const query: Query = { filter: { relationName: { eq: 'foo' } } }; - assertOneToManySQL( - testEntity, - 'testRelations', - query, - ` WHERE ((${oneToManyEntityClause})) AND (("testRelations"."testRelationPk" IN (SELECT "testRelations"."testRelationPk" AS "testRelations_testRelationPk" FROM "test_relation" "testRelations" WHERE ${oneToManyEntityClause} AND ("testRelations"."relation_name" = ?)))) ${orderByTestRelationsTestEntityId}`, - [testEntity.testEntityPk, testEntity.testEntityPk, 'foo'], - ); + assertOneToManySQL(testEntity, 'testRelations', query, ` AND ("testRelations"."relation_name" = ?)`, [ + testEntity.testEntityPk, + 'foo', + ]); }); }); describe('with paging', () => { - const pagingBaseFragment = ` WHERE ((${oneToManyEntityClause})) AND (("testRelations"."testRelationPk" IN (SELECT "testRelations"."testRelationPk" AS "testRelations_testRelationPk" FROM "test_relation" "testRelations" WHERE ${oneToManyEntityClause}`; - - it('should apply empty paging args', () => { - assertOneToManySQL( - testEntity, - 'testRelations', - {}, - `${pagingBaseFragment}))) ${orderByTestRelationsTestEntityId}`, - [testEntity.testEntityPk, testEntity.testEntityPk], - ); - }); - it('should apply paging args going forward', () => { assertOneToManySQL( testEntity, @@ -372,8 +197,8 @@ describe('RelationQueryBuilder', (): void => { offset: 11, }, }, - `${pagingBaseFragment} LIMIT 10 OFFSET 11))) ${orderByTestRelationsTestEntityId}`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` LIMIT 10 OFFSET 11`, + [testEntity.testEntityPk], ); }); @@ -387,14 +212,13 @@ describe('RelationQueryBuilder', (): void => { offset: 10, }, }, - `${pagingBaseFragment} LIMIT 10 OFFSET 10))) ${orderByTestRelationsTestEntityId}`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` LIMIT 10 OFFSET 10`, + [testEntity.testEntityPk], ); }); }); describe('with sorting', () => { - const sortingBaseFragment = ` WHERE ((${oneToManyEntityClause})) AND ((${baseOneToManySubQuery})) ${orderByTestRelationsTestEntityId}`; it('should apply ASC sorting', () => { assertOneToManySQL( testEntity, @@ -402,8 +226,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.ASC }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" ASC`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" ASC`, + [testEntity.testEntityPk], ); }); @@ -414,8 +238,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.ASC, nulls: SortNulls.NULLS_FIRST }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" ASC NULLS FIRST`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" ASC NULLS FIRST`, + [testEntity.testEntityPk], ); }); @@ -426,8 +250,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.ASC, nulls: SortNulls.NULLS_LAST }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" ASC NULLS LAST`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" ASC NULLS LAST`, + [testEntity.testEntityPk], ); }); @@ -438,8 +262,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.DESC }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" DESC`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" DESC`, + [testEntity.testEntityPk], ); }); @@ -450,8 +274,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.DESC, nulls: SortNulls.NULLS_FIRST }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" DESC NULLS FIRST`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" DESC NULLS FIRST`, + [testEntity.testEntityPk], ); }); @@ -462,8 +286,8 @@ describe('RelationQueryBuilder', (): void => { { sorting: [{ field: 'relationName', direction: SortDirection.DESC, nulls: SortNulls.NULLS_LAST }], }, - `${sortingBaseFragment}, "testRelations"."relation_name" DESC NULLS LAST`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" DESC NULLS LAST`, + [testEntity.testEntityPk], ); }); @@ -477,104 +301,10 @@ describe('RelationQueryBuilder', (): void => { { field: 'testRelationPk', direction: SortDirection.DESC }, ], }, - `${sortingBaseFragment}, "testRelations"."relation_name" ASC, "testRelations"."testRelationPk" DESC`, - [testEntity.testEntityPk, testEntity.testEntityPk], + ` ORDER BY "testRelations"."relation_name" ASC, "testRelations"."testRelationPk" DESC`, + [testEntity.testEntityPk], ); }); }); }); - - describe('isEntityIdCol', () => { - it('should return true if the name is an entityId column name', () => { - expect(RelationQueryBuilder.isEntityIdCol('__nestjsQueryEntityId_fooId__')).toBe(true); - expect(RelationQueryBuilder.isEntityIdCol('__nestjsQueryEntityId_foo_id__')).toBe(true); - expect(RelationQueryBuilder.isEntityIdCol('__nestjsQueryEntityId_foo.id__')).toBe(true); - }); - - it('should return false if the name is not an entityId column name', () => { - expect(RelationQueryBuilder.isEntityIdCol('nestjsQueryEntityId_fooId__')).toBe(false); - expect(RelationQueryBuilder.isEntityIdCol('__nestjsQueryEntityId__')).toBe(false); - expect(RelationQueryBuilder.isEntityIdCol('__nestjsQueryEntityId_foo.id')).toBe(false); - }); - }); - - describe('parseEntityIdFromName', () => { - it('should return the column name', () => { - expect(RelationQueryBuilder.parseEntityIdFromName('__nestjsQueryEntityId_fooId__')).toBe('fooId'); - expect(RelationQueryBuilder.parseEntityIdFromName('__nestjsQueryEntityId_foo_id__')).toBe('foo_id'); - expect(RelationQueryBuilder.parseEntityIdFromName('__nestjsQueryEntityId_foo.id__')).toBe('foo.id'); - }); - }); - - describe('getRelationPrimaryKeysPropertyNameAndColumnsName', () => { - it('should return the primary key propertyName and selected column name on owning side', () => { - const oneToManyPks = getRelationQueryBuilder( - TestEntity, - 'testRelations', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(oneToManyPks).toEqual([ - { - columnName: 'testRelations_testRelationPk', - propertyName: 'testRelationPk', - }, - ]); - - const manyToManyOwnerPks = getRelationQueryBuilder( - TestEntity, - 'manyTestRelations', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(manyToManyOwnerPks).toEqual([ - { - columnName: 'manyTestRelations_testRelationPk', - propertyName: 'testRelationPk', - }, - ]); - - const oneToOneOwnerPks = getRelationQueryBuilder( - TestEntity, - 'oneTestRelation', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(oneToOneOwnerPks).toEqual([ - { - columnName: 'oneTestRelation_testRelationPk', - propertyName: 'testRelationPk', - }, - ]); - }); - - it('should return the primary key propertyName and selected column name on none owning side', () => { - const oneToManyPks = getRelationQueryBuilder( - TestRelation, - 'testEntity', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(oneToManyPks).toEqual([ - { - columnName: 'testEntity_testEntityPk', - propertyName: 'testEntityPk', - }, - ]); - - const manyToManyOwnerPks = getRelationQueryBuilder( - TestRelation, - 'manyTestEntities', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(manyToManyOwnerPks).toEqual([ - { - columnName: 'manyTestEntities_testEntityPk', - propertyName: 'testEntityPk', - }, - ]); - - const oneToOneOwnerPks = getRelationQueryBuilder( - TestRelation, - 'oneTestEntity', - ).getRelationPrimaryKeysPropertyNameAndColumnsName(); - expect(oneToOneOwnerPks).toEqual([ - { - columnName: 'oneTestEntity_testEntityPk', - propertyName: 'testEntityPk', - }, - ]); - }); - }); }); diff --git a/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts b/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts index 53411e1a2..bfbc57472 100644 --- a/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/sql-comparison.builder.spec.ts @@ -1,13 +1,8 @@ -import { closeTestConnection, createTestConnection, getTestConnection } from '../__fixtures__/connection.fixture'; import { TestEntity } from '../__fixtures__/test.entity'; import { SQLComparisionBuilder } from '../../src/query'; describe('SQLComparisionBuilder', (): void => { - beforeEach(createTestConnection); - afterEach(closeTestConnection); - - const getRepo = () => getTestConnection().getRepository(TestEntity); - const createSQLComparisionBuilder = () => new SQLComparisionBuilder(getRepo()); + const createSQLComparisionBuilder = () => new SQLComparisionBuilder(); it('should throw an error for an invalid comparison type', () => { // @ts-ignore diff --git a/packages/query-typeorm/__tests__/query/where.builder.spec.ts b/packages/query-typeorm/__tests__/query/where.builder.spec.ts index bea00cc88..80b25a615 100644 --- a/packages/query-typeorm/__tests__/query/where.builder.spec.ts +++ b/packages/query-typeorm/__tests__/query/where.builder.spec.ts @@ -19,7 +19,7 @@ describe('WhereBuilder', (): void => { const getRepo = () => getTestConnection().getRepository(TestEntity); const getQueryBuilder = () => getRepo().createQueryBuilder(); - const createWhereBuilder = () => new WhereBuilder(getRepo()); + const createWhereBuilder = () => new WhereBuilder(); const assertSQL = (filter: Filter, expectedSql: string, expectedArgs: any[]): void => { const selectQueryBuilder = createWhereBuilder().build(getQueryBuilder(), filter, 'TestEntity'); diff --git a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts index bdd81ecc1..7af642c6b 100644 --- a/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts +++ b/packages/query-typeorm/__tests__/services/typeorm-query.service.spec.ts @@ -105,30 +105,19 @@ describe('TypeOrmQueryService', (): void => { const entities = testEntities(); const entityOneRelations = testRelations(entities[0].testEntityPk); const entityTwoRelations = testRelations(entities[1].testEntityPk); - const allRelations = [...entityOneRelations, ...entityTwoRelations]; const { queryService, mockRepo, mockRelationQueryBuilder } = createQueryService(); const relationQueryBuilder: SelectQueryBuilder = mock(SelectQueryBuilder); when(mockRepo.target).thenReturn(TestEntity); // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); - when(relationQueryBuilder.getRawAndEntities()).thenResolve({ - raw: allRelations.map((r) => ({ - // eslint-disable-next-line @typescript-eslint/camelcase - testRelation_testRelationPk: r.testRelationPk, - // eslint-disable-next-line @typescript-eslint/camelcase - __nestjsQueryEntityId_testEntityPk__: r.testEntityId, - })), - entities: allRelations, - }); - when( - mockRelationQueryBuilder.select(objectContaining(entities), objectContaining({ paging: { limit: 2 } })), - ).thenReturn(instance(relationQueryBuilder)); - when(mockRelationQueryBuilder.getRelationPrimaryKeysPropertyNameAndColumnsName()).thenReturn([ - { - propertyName: 'testRelationPk', - columnName: 'testRelation_testRelationPk', - }, - ]); + when(mockRelationQueryBuilder.select(entities[0], objectContaining({ paging: { limit: 2 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(mockRelationQueryBuilder.select(entities[1], objectContaining({ paging: { limit: 2 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(relationQueryBuilder.getMany()).thenResolve(entityOneRelations).thenResolve(entityTwoRelations); + // @ts-ignore const queryResult = await queryService.queryRelations(TestRelation, relationName, entities, { paging: { limit: 2 }, @@ -149,29 +138,23 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); - when(relationQueryBuilder.getRawAndEntities()).thenResolve({ - raw: entityOneRelations.map((r) => ({ - // eslint-disable-next-line @typescript-eslint/camelcase - testRelation_testRelationPk: r.testRelationPk, - // eslint-disable-next-line @typescript-eslint/camelcase - __nestjsQueryEntityId_testEntityPk__: r.testEntityId, - })), - entities: entityOneRelations, - }); - when( - mockRelationQueryBuilder.select(objectContaining(entities), objectContaining({ paging: { limit: 2 } })), - ).thenReturn(instance(relationQueryBuilder)); - when(mockRelationQueryBuilder.getRelationPrimaryKeysPropertyNameAndColumnsName()).thenReturn([ - { - propertyName: 'testRelationPk', - columnName: 'testRelation_testRelationPk', - }, - ]); + when(mockRelationQueryBuilder.select(entities[0], objectContaining({ paging: { limit: 2 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(mockRelationQueryBuilder.select(entities[1], objectContaining({ paging: { limit: 2 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(relationQueryBuilder.getMany()).thenResolve(entityOneRelations).thenResolve([]); // @ts-ignore const queryResult = await queryService.queryRelations(TestRelation, relationName, entities, { paging: { limit: 2 }, }); - return expect(queryResult).toEqual(new Map([[entities[0], entityOneRelations]])); + return expect(queryResult).toEqual( + new Map([ + [entities[0], entityOneRelations], + [entities[1], []], + ]), + ); }); }); }); @@ -240,33 +223,13 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); - when(relationQueryBuilder.getRawAndEntities()).thenResolve({ - raw: [ - { - // eslint-disable-next-line @typescript-eslint/camelcase - testRelation_testRelationPk: entityOneRelation.testRelationPk, - // eslint-disable-next-line @typescript-eslint/camelcase - __nestjsQueryEntityId_testEntityPk__: entityOneRelation.testEntityId, - }, - { - // eslint-disable-next-line @typescript-eslint/camelcase - testRelation_testRelationPk: entityTwoRelation.testRelationPk, - // eslint-disable-next-line @typescript-eslint/camelcase - __nestjsQueryEntityId_testEntityPk__: entityTwoRelation.testEntityId, - }, - ], - entities: [entityOneRelation, entityTwoRelation], - }); - when( - mockRelationQueryBuilder.select(objectContaining(entities), objectContaining({ paging: { limit: 1 } })), - ).thenReturn(instance(relationQueryBuilder)); - when(mockRelationQueryBuilder.getRelationPrimaryKeysPropertyNameAndColumnsName()).thenReturn([ - { - propertyName: 'testRelationPk', - columnName: 'testRelation_testRelationPk', - }, - ]); - // @ts-ignore + when(mockRelationQueryBuilder.select(entities[0], objectContaining({ paging: { limit: 1 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(mockRelationQueryBuilder.select(entities[1], objectContaining({ paging: { limit: 1 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(relationQueryBuilder.getMany()).thenResolve([entityOneRelation]).thenResolve([entityTwoRelation]); const queryResult = await queryService.findRelation(TestRelation, relationName, entities); return expect(queryResult).toEqual( new Map([ @@ -284,29 +247,20 @@ describe('TypeOrmQueryService', (): void => { when(mockRepo.target).thenReturn(TestEntity); // @ts-ignore when(mockRepo.metadata).thenReturn({ relations: [{ propertyName: relationName, type: TestRelation }] }); - when(relationQueryBuilder.getRawAndEntities()).thenResolve({ - raw: [ - { - // eslint-disable-next-line @typescript-eslint/camelcase - testRelation_testRelationPk: entityOneRelation.testRelationPk, - // eslint-disable-next-line @typescript-eslint/camelcase - __nestjsQueryEntityId_testEntityPk__: entityOneRelation.testEntityId, - }, - ], - entities: [entityOneRelation], - }); - when( - mockRelationQueryBuilder.select(objectContaining(entities), objectContaining({ paging: { limit: 1 } })), - ).thenReturn(instance(relationQueryBuilder)); - when(mockRelationQueryBuilder.getRelationPrimaryKeysPropertyNameAndColumnsName()).thenReturn([ - { - propertyName: 'testRelationPk', - columnName: 'testRelation_testRelationPk', - }, - ]); - // @ts-ignore + when(mockRelationQueryBuilder.select(entities[0], objectContaining({ paging: { limit: 1 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(mockRelationQueryBuilder.select(entities[1], objectContaining({ paging: { limit: 1 } }))).thenReturn( + instance(relationQueryBuilder), + ); + when(relationQueryBuilder.getMany()).thenResolve([entityOneRelation]).thenResolve([]); const queryResult = await queryService.findRelation(TestRelation, relationName, entities); - return expect(queryResult).toEqual(new Map([[entities[0], entityOneRelation]])); + return expect(queryResult).toEqual( + new Map([ + [entities[0], entityOneRelation], + [entities[1], undefined], + ]), + ); }); }); }); diff --git a/packages/query-typeorm/package.json b/packages/query-typeorm/package.json index 28d413d4a..51899daa6 100644 --- a/packages/query-typeorm/package.json +++ b/packages/query-typeorm/package.json @@ -18,8 +18,7 @@ "access": "public" }, "dependencies": { - "@nestjs-query/core": "^0.8.0", - "lodash.filter": "^4.6.0" + "@nestjs-query/core": "^0.8.0" }, "peerDependencies": { "@nestjs/common": "^7.0.6", @@ -30,7 +29,6 @@ "devDependencies": { "@nestjs/common": "^7.0.6", "@nestjs/typeorm": "^7.0.0", - "@types/lodash.filter": "^4.6.6", "class-transformer": "^0.2.3", "sqlite3": "^4.1.1", "ts-mockito": "^2.5.0", diff --git a/packages/query-typeorm/src/query/filter-query.builder.ts b/packages/query-typeorm/src/query/filter-query.builder.ts index 174081b34..27f4a81d2 100644 --- a/packages/query-typeorm/src/query/filter-query.builder.ts +++ b/packages/query-typeorm/src/query/filter-query.builder.ts @@ -8,7 +8,6 @@ import { WhereExpression, } from 'typeorm'; import { WhereBuilder } from './where.builder'; -import { AbstractQueryBuilder } from './query-builder.abstract'; /** * @internal @@ -34,13 +33,11 @@ interface Pageable extends QueryBuilder { * * Class that will convert a Query into a `typeorm` Query Builder. */ -export class FilterQueryBuilder extends AbstractQueryBuilder { +export class FilterQueryBuilder { constructor( readonly repo: Repository, - readonly whereBuilder: WhereBuilder = new WhereBuilder(repo), - ) { - super(repo); - } + readonly whereBuilder: WhereBuilder = new WhereBuilder(), + ) {} /** * Create a `typeorm` SelectQueryBuilder with `WHERE`, `ORDER BY` and `LIMIT/OFFSET` clauses. diff --git a/packages/query-typeorm/src/query/query-builder.abstract.ts b/packages/query-typeorm/src/query/query-builder.abstract.ts deleted file mode 100644 index 5df5768ae..000000000 --- a/packages/query-typeorm/src/query/query-builder.abstract.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { EntityMetadata, Repository } from 'typeorm'; - -/** - * @internal - * - * Base class for all QueryBuilders. - */ -export abstract class AbstractQueryBuilder { - readonly entityMetadata: EntityMetadata; - - protected constructor(readonly repository: Repository) { - this.entityMetadata = this.repository.metadata; - } - - /** - * Escapes a string to be safe to in a query. - * @param str - the string to escape. - */ - protected escape(str: string): string { - return this.repository.manager.connection.driver.escape(str); - } -} diff --git a/packages/query-typeorm/src/query/relation-query.builder.ts b/packages/query-typeorm/src/query/relation-query.builder.ts index 245b4130f..bce413dc1 100644 --- a/packages/query-typeorm/src/query/relation-query.builder.ts +++ b/packages/query-typeorm/src/query/relation-query.builder.ts @@ -2,9 +2,7 @@ import { Class, Query } from '@nestjs-query/core'; import { Repository, SelectQueryBuilder, ObjectLiteral, Brackets } from 'typeorm'; import { RelationMetadata } from 'typeorm/metadata/RelationMetadata'; -import { DriverUtils } from 'typeorm/driver/DriverUtils'; import { FilterQueryBuilder } from './filter-query.builder'; -import { AbstractQueryBuilder } from './query-builder.abstract'; interface JoinCondition { leftHand: string; @@ -21,20 +19,11 @@ type WhereCondition = { params: ObjectLiteral; }; -type PrimaryKey = { - databasePath: string; - selectPath: string; - propertyName: string; -}; - interface RelationQuery { from: Class; fromAlias: string; - fromPrimaryKeys: PrimaryKey[]; - entityIds: string[]; - entityIdSelects: string[]; joins: JoinColumn[]; - whereCondition(entity: Entity, entityIndex: number): WhereCondition; + whereCondition(entity: Entity): WhereCondition; } /** @@ -42,7 +31,7 @@ interface RelationQuery { * * Class that will convert a Query into a `typeorm` Query Builder. */ -export class RelationQueryBuilder extends AbstractQueryBuilder { +export class RelationQueryBuilder { private relationMetadata: RelationQuery | undefined; readonly filterQueryBuilder: FilterQueryBuilder; @@ -50,77 +39,28 @@ export class RelationQueryBuilder extends AbstractQueryBuilder readonly relationRepo: Repository; constructor(readonly repo: Repository, readonly relation: string) { - super(repo); this.relationRepo = this.repo.manager.getRepository(this.relationMeta.from); this.filterQueryBuilder = new FilterQueryBuilder(this.relationRepo); } - select(entity: Entity | Entity[], query: Query): SelectQueryBuilder { - const entityArr = Array.isArray(entity) ? entity : [entity]; - const relationBuilder = this.createRelationQueryBuilder(entityArr); - return this.applyRelationFilter(relationBuilder, entityArr, query); - } - - private applyRelationFilter( - baseBuilder: SelectQueryBuilder, - entityArr: Entity[], - query: Query, - ): SelectQueryBuilder { - const { entityIds, fromPrimaryKeys, entityIdSelects } = this.relationMeta; - const parameters = {}; - const fromPrimaryKeysIn = - fromPrimaryKeys.length === 1 - ? fromPrimaryKeys[0].selectPath - : `(${fromPrimaryKeys.map((pk) => pk.selectPath).join(', ')})`; - const subQueries = entityArr.reduce((sqls, e, index) => { - const { subQuery, params } = this.createRelationSubQuery(e, index); - // only apply filter and paging - let filteredSubQuery = this.filterQueryBuilder.applyFilter(subQuery, query.filter, subQuery.alias); - filteredSubQuery = this.filterQueryBuilder.applyPaging(filteredSubQuery, query.paging); - Object.assign(parameters, params, filteredSubQuery.getParameters()); - return [...sqls, `(${fromPrimaryKeysIn} IN (${filteredSubQuery.getQuery()}))`]; - }, [] as string[]); - const oredBuilder = baseBuilder.andWhere(`(${subQueries.join(' OR ')})`, parameters); - // force sorting by entity pks first so sub results are sorted properly - const sortedBuilder = entityIds - .reduce((builder, pk) => { - return builder.addOrderBy(pk, 'ASC'); - }, oredBuilder) - .addSelect(entityIdSelects); - - return this.filterQueryBuilder.applySorting(sortedBuilder, query.sorting, sortedBuilder.alias); + select(entity: Entity, query: Query): SelectQueryBuilder { + let relationBuilder = this.createRelationQueryBuilder(entity); + relationBuilder = this.filterQueryBuilder.applyFilter(relationBuilder, query.filter, relationBuilder.alias); + relationBuilder = this.filterQueryBuilder.applyPaging(relationBuilder, query.paging); + return this.filterQueryBuilder.applySorting(relationBuilder, query.sorting, relationBuilder.alias); } - private createRelationSubQuery( - e: Entity, - entityIndex: number, - ): { subQuery: SelectQueryBuilder; params: ObjectLiteral } { - const meta = this.relationMeta; - const baseBuilder = this.createRelationQueryBuilder().select(meta.fromPrimaryKeys.map((pk) => pk.selectPath)); - const { sql, params } = meta.whereCondition(e, entityIndex); - return { subQuery: baseBuilder.where(sql), params }; - } - - private createRelationQueryBuilder(entities?: Entity[]): SelectQueryBuilder { + private createRelationQueryBuilder(entity: Entity): SelectQueryBuilder { const meta = this.relationMeta; const queryBuilder = this.relationRepo.createQueryBuilder(meta.fromAlias); const joinedBuilder = meta.joins.reduce((qb, join) => { const conditions = join.conditions.map(({ leftHand, rightHand }) => `${leftHand} = ${rightHand}`); return qb.innerJoin(join.target, join.alias, conditions.join(' AND ')); }, queryBuilder); - if (!entities || !entities.length) { - return joinedBuilder; - } - return joinedBuilder.andWhere( - new Brackets((andBuilder) => { - return entities.reduce((qb, entity, entityIndex) => { - return qb.orWhere( - new Brackets((bqb) => { - const where = meta.whereCondition(entity, entityIndex); - bqb.orWhere(where.sql, where.params); - }), - ); - }, andBuilder); + return joinedBuilder.where( + new Brackets((bqb) => { + const where = meta.whereCondition(entity); + bqb.andWhere(where.sql, where.params); }), ); } @@ -148,11 +88,6 @@ export class RelationQueryBuilder extends AbstractQueryBuilder getManyToOneOrOneToOneOwnerMeta(relation: RelationMetadata): RelationQuery { const aliasName = relation.entityMetadata.name; const { primaryColumns } = relation.entityMetadata; - const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({ - selectPath: `${relation.propertyName}.${pk.propertyName}`, - databasePath: pk.databasePath, - propertyName: pk.propertyName, - })); const { joinColumns } = relation; const joins: JoinColumn[] = [ { @@ -164,22 +99,15 @@ export class RelationQueryBuilder extends AbstractQueryBuilder })), }, ]; - const entityIds = primaryColumns.map((column) => `${aliasName}.${column.propertyPath}`); - const entityIdSelects = primaryColumns.map( - (column) => `${aliasName}.${column.propertyPath} AS ${this.createEntityIdSelect(column.propertyPath)}`, - ); return { from: relation.type as Class, fromAlias: relation.propertyName, - fromPrimaryKeys, - entityIds, - entityIdSelects, joins, - whereCondition(entity: Entity, entityIndex: number): WhereCondition { + whereCondition(entity: Entity): WhereCondition { const params: ObjectLiteral = {}; const sql = primaryColumns .map((column, columnIndex) => { - const paramName = `${aliasName}_entity_${entityIndex}_${columnIndex}`; + const paramName = `${aliasName}_entity_${columnIndex}`; params[paramName] = column.getEntityValue(entity); return `${aliasName}.${column.propertyPath} = :${paramName}`; }) @@ -192,30 +120,15 @@ export class RelationQueryBuilder extends AbstractQueryBuilder getOneToManyOrOneToOneNotOwnerMeta(relation: RelationMetadata): RelationQuery { const aliasName = relation.propertyName; const columns = relation.inverseRelation!.joinColumns; - const fromPrimaryKeys: PrimaryKey[] = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({ - selectPath: `${aliasName}.${pk.propertyName}`, - databasePath: pk.databasePath, - propertyName: pk.propertyName, - })); - const entityIds = columns.map((joinColumn) => `${aliasName}.${joinColumn.propertyPath}`); - const entityIdSelects = columns.map( - (joinColumn) => - `${aliasName}.${joinColumn.propertyPath} AS ${this.createEntityIdSelect( - joinColumn.referencedColumn!.propertyPath, - )}`, - ); return { from: relation.inverseRelation!.entityMetadata.target as Class, fromAlias: aliasName, - fromPrimaryKeys, - entityIds, - entityIdSelects, joins: [], - whereCondition(entity: Entity, entityIndex: number): WhereCondition { + whereCondition(entity: Entity): WhereCondition { const params: ObjectLiteral = {}; const sql = columns .map((col, colIndex: number) => { - const paramName = `${aliasName}_entity_${entityIndex}_${colIndex}`; + const paramName = `${aliasName}_entity_${colIndex}`; params[paramName] = col.referencedColumn!.getEntityValue(entity); return `${aliasName}.${col.propertyPath} = :${paramName}`; }) @@ -238,30 +151,15 @@ export class RelationQueryBuilder extends AbstractQueryBuilder })), }, ]; - const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({ - selectPath: `${mainAlias}.${pk.propertyName}`, - databasePath: pk.databasePath, - propertyName: pk.propertyName, - })); - const entityIds = relation.joinColumns.map((joinColumn) => `${joinAlias}.${joinColumn.propertyName}`); - const entityIdSelects = relation.joinColumns.map( - (joinColumn) => - `${joinAlias}.${joinColumn.propertyName} AS ${this.createEntityIdSelect( - joinColumn.referencedColumn!.propertyPath, - )}`, - ); return { from: relation.type as Class, fromAlias: mainAlias, - fromPrimaryKeys, - entityIds, - entityIdSelects, joins, - whereCondition(entity: Entity, entityIndex: number): WhereCondition { + whereCondition(entity: Entity): WhereCondition { const params: ObjectLiteral = {}; const sql = relation.joinColumns .map((joinColumn) => { - const paramName = `${joinColumn.propertyName}_${entityIndex}`; + const paramName = joinColumn.propertyName; params[paramName] = joinColumn.referencedColumn!.getEntityValue(entity); return `${joinAlias}.${joinColumn.propertyName} = :${paramName}`; }) @@ -284,32 +182,15 @@ export class RelationQueryBuilder extends AbstractQueryBuilder })), }, ]; - const fromPrimaryKeys = relation.inverseRelation!.entityMetadata.primaryColumns.map((pk) => ({ - selectPath: `${mainAlias}.${pk.propertyName}`, - databasePath: pk.databasePath, - propertyName: pk.propertyName, - })); - const entityIds = relation.inverseRelation!.inverseJoinColumns.map( - (inverseJoinColumn) => `${joinAlias}.${inverseJoinColumn.propertyName}`, - ); - const entityIdSelects = relation.inverseRelation!.inverseJoinColumns.map( - (inverseJoinColumn) => - `${joinAlias}.${inverseJoinColumn.propertyName} AS ${this.createEntityIdSelect( - inverseJoinColumn.referencedColumn!.propertyPath, - )}`, - ); return { from: relation.type as Class, fromAlias: mainAlias, - fromPrimaryKeys, - entityIds, - entityIdSelects, joins, - whereCondition(entity: Entity, entityIndex: number): WhereCondition { + whereCondition(entity: Entity): WhereCondition { const params: ObjectLiteral = {}; const sql = relation .inverseRelation!.inverseJoinColumns.map((inverseJoinColumn) => { - const paramName = `${inverseJoinColumn.propertyName}_${entityIndex}`; + const paramName = `${inverseJoinColumn.propertyName}`; params[paramName] = inverseJoinColumn.referencedColumn!.getEntityValue(entity); return `${joinAlias}.${inverseJoinColumn.propertyName} = :${paramName}`; }) @@ -318,27 +199,4 @@ export class RelationQueryBuilder extends AbstractQueryBuilder }, }; } - - private createEntityIdSelect(propertyPath: string): string { - return this.escape(`__nestjsQueryEntityId_${propertyPath}__`); - } - - static isEntityIdCol(colName: string): boolean { - return /^__nestjsQueryEntityId_(.*)__$/.test(colName); - } - - static parseEntityIdFromName(colName: string): string { - return colName.replace(/^__nestjsQueryEntityId_/, '').replace(/__$/, ''); - } - - getRelationPrimaryKeysPropertyNameAndColumnsName(): { columnName: string; propertyName: string }[] { - return this.relationMeta.fromPrimaryKeys.map((pk) => ({ - propertyName: pk.propertyName, - columnName: DriverUtils.buildColumnAlias( - this.relationRepo.manager.connection.driver, - this.relationMeta.fromAlias, - pk.databasePath, - ), - })); - } } diff --git a/packages/query-typeorm/src/query/sql-comparison.builder.ts b/packages/query-typeorm/src/query/sql-comparison.builder.ts index ce186b7b8..ee653155b 100644 --- a/packages/query-typeorm/src/query/sql-comparison.builder.ts +++ b/packages/query-typeorm/src/query/sql-comparison.builder.ts @@ -1,6 +1,5 @@ -import { ObjectLiteral, Repository } from 'typeorm'; +import { ObjectLiteral } from 'typeorm'; import { FilterComparisonOperators } from '@nestjs-query/core'; -import { AbstractQueryBuilder } from './query-builder.abstract'; /** * @internal @@ -16,7 +15,7 @@ export type EntityComparisonField = Entity[F] | * @internal * Builder to create SQL Comparisons. (=, !=, \>, etc...) */ -export class SQLComparisionBuilder extends AbstractQueryBuilder { +export class SQLComparisionBuilder { paramCount = 0; static DEFAULT_COMPARISON_MAP: Record = { @@ -32,12 +31,7 @@ export class SQLComparisionBuilder extends AbstractQueryBuilder notilike: 'NOT ILIKE', }; - constructor( - repo: Repository, - readonly comparisonMap: Record = SQLComparisionBuilder.DEFAULT_COMPARISON_MAP, - ) { - super(repo); - } + constructor(readonly comparisonMap: Record = SQLComparisionBuilder.DEFAULT_COMPARISON_MAP) {} private get paramName(): string { const param = `param${this.paramCount}`; diff --git a/packages/query-typeorm/src/query/where.builder.ts b/packages/query-typeorm/src/query/where.builder.ts index bad982a02..483954178 100644 --- a/packages/query-typeorm/src/query/where.builder.ts +++ b/packages/query-typeorm/src/query/where.builder.ts @@ -1,4 +1,4 @@ -import { Brackets, Repository, WhereExpression } from 'typeorm'; +import { Brackets, WhereExpression } from 'typeorm'; import { Filter, FilterComparisonOperators, FilterComparisons, FilterFieldComparison } from '@nestjs-query/core'; import { EntityComparisonField, SQLComparisionBuilder } from './sql-comparison.builder'; @@ -7,10 +7,7 @@ import { EntityComparisonField, SQLComparisionBuilder } from './sql-comparison.b * Builds a WHERE clause from a Filter. */ export class WhereBuilder { - constructor( - readonly repository: Repository, - readonly sqlComparisionBuilder: SQLComparisionBuilder = new SQLComparisionBuilder(repository), - ) {} + constructor(readonly sqlComparisionBuilder: SQLComparisionBuilder = new SQLComparisionBuilder()) {} /** * Builds a WHERE clause from a Filter. diff --git a/packages/query-typeorm/src/services/relation-query.service.ts b/packages/query-typeorm/src/services/relation-query.service.ts index 9d71003c5..479d30b6a 100644 --- a/packages/query-typeorm/src/services/relation-query.service.ts +++ b/packages/query-typeorm/src/services/relation-query.service.ts @@ -1,6 +1,5 @@ import { Query, Class, AssemblerFactory } from '@nestjs-query/core'; -import { Repository, RelationQueryBuilder as TypeOrmRelationQueryBuilder, ObjectLiteral } from 'typeorm'; -import lodashFilter from 'lodash.filter'; +import { Repository, RelationQueryBuilder as TypeOrmRelationQueryBuilder } from 'typeorm'; import { FilterQueryBuilder, RelationQueryBuilder } from '../query'; /** @@ -175,17 +174,15 @@ export abstract class RelationQueryService { ): Promise> { const assembler = AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName)); const relationQueryBuilder = this.getRelationQueryBuilder(relationName); - const relations = await relationQueryBuilder.select(entities, assembler.convertQuery(query)).getRawAndEntities(); - return relations.raw.reduce((results, rawRelation) => { - this.getEntityFromFromResult(rawRelation, entities).forEach((e) => { - if (!results.has(e)) { - results.set(e, []); - } - const relationDtos = assembler.convertToDTOs( - this.getRelationsFromPrimaryKeys(relationQueryBuilder, rawRelation, relations.entities), - ); - results.get(e).push(...relationDtos); - }); + const convertedQuery = assembler.convertQuery(query); + const entityRelations = await Promise.all( + entities.map((e) => { + return relationQueryBuilder.select(e, convertedQuery).getMany(); + }), + ); + return entityRelations.reduce((results, relations, index) => { + const e = entities[index]; + results.set(e, assembler.convertToDTOs(relations)); return results; }, new Map()); } @@ -222,28 +219,4 @@ export abstract class RelationQueryService { } return relationMeta.type as Class; } - - private getEntityFromFromResult(rawResult: ObjectLiteral, entities: Entity[]): Entity[] { - const pks = Object.keys(rawResult) - .filter((key) => RelationQueryBuilder.isEntityIdCol(key)) - .reduce((keys, key) => { - const entityProp = RelationQueryBuilder.parseEntityIdFromName(key); - return { ...keys, [entityProp]: rawResult[key] }; - }, {} as Partial); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return lodashFilter(entities as any[], pks); - } - - private getRelationsFromPrimaryKeys( - relationBuilder: RelationQueryBuilder, - rawResult: ObjectLiteral, - relations: Relation[], - ): Relation[] { - const pks = relationBuilder.getRelationPrimaryKeysPropertyNameAndColumnsName(); - const filter = pks.reduce((keys, key) => { - return { ...keys, [key.propertyName]: rawResult[key.columnName] }; - }, {} as Partial); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return lodashFilter(relations as any[], filter); - } }