feat: add support for range filters

This commit is contained in:
Wieland Schöbl
2020-11-30 16:21:04 +01:00
committed by Rainer Killinger
parent 785813c3fb
commit dcf7906f79
5 changed files with 466 additions and 136 deletions

277
package-lock.json generated
View File

@@ -614,33 +614,210 @@
}
},
"@openstapps/core": {
"version": "0.38.0",
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.38.0.tgz",
"integrity": "sha512-wFseZk5UtlLzVYoVPu+2YVLb0Ow7Lh4xVVW5Nsoew9Bft59sVZdroSPIPXqWWApQ40R0CVWxwxng0soO4bZRbw==",
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.41.0.tgz",
"integrity": "sha512-cWzyVVUrQCrHk5EEuoWLfLt6cKLcDwoLY1UPxqschqeDmI+zlqBA0vnOsmAgVXZxv2+msFRYbkCiUd3Mofdzvg==",
"requires": {
"@openstapps/core-tools": "0.16.0",
"@types/geojson": "1.0.6",
"@types/json-patch": "0.0.30",
"@types/json-schema": "7.0.6",
"@types/node": "10.17.14",
"@types/node": "10.17.44",
"fast-clone": "1.5.13",
"http-status-codes": "2.1.2",
"http-status-codes": "2.1.4",
"json-patch": "0.7.0",
"json-schema": "0.2.5",
"ts-optchain": "0.1.8"
},
"dependencies": {
"@openstapps/core-tools": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@openstapps/core-tools/-/core-tools-0.16.0.tgz",
"integrity": "sha512-agcRBjavO7TlgjDvoJEnUgXzDOW5w6us2VENZ73DUC+8QKc9okdjDuVIMy2oBrKGgOxJBg0G/xKqQN9CARqqAg==",
"requires": {
"@krlwlfrt/async-pool": "0.3.0",
"@openstapps/logger": "0.5.0",
"@types/glob": "7.1.1",
"@types/got": "9.6.9",
"@types/json-schema": "7.0.6",
"@types/mustache": "4.0.0",
"@types/node": "10.17.21",
"ajv": "6.11.0",
"better-ajv-errors": "0.6.7",
"chai": "4.2.0",
"commander": "4.1.1",
"deepmerge": "4.2.2",
"del": "5.1.0",
"flatted": "2.0.1",
"glob": "7.1.6",
"got": "10.5.5",
"humanize-string": "2.1.0",
"json-schema": "0.2.5",
"mustache": "4.0.0",
"plantuml-encoder": "1.4.0",
"toposort": "2.0.2",
"ts-json-schema-generator": "0.60.0",
"ts-node": "8.6.2",
"typedoc": "0.18.0",
"typescript": "3.8.3"
},
"dependencies": {
"@types/node": {
"version": "10.17.21",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.21.tgz",
"integrity": "sha512-PQKsydPxYxF1DsAFWmunaxd3sOi3iMt6Zmx/tgaagHYmwJ/9cRH91hQkeJZaUGWbvn0K5HlSVEXkn5U/llWPpQ=="
}
}
},
"@sindresorhus/is": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-1.2.0.tgz",
"integrity": "sha512-mwhXGkRV5dlvQc4EgPDxDxO6WuMBVymGFd1CA+2Y+z5dG9MNspoQ+AWjl/Ld1MnpCL8AKbosZlDVohqcIwuWsw=="
},
"@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"requires": {
"defer-to-connect": "^2.0.0"
}
},
"@types/got": {
"version": "9.6.9",
"resolved": "https://registry.npmjs.org/@types/got/-/got-9.6.9.tgz",
"integrity": "sha512-w+ZE+Ovp6fM+1sHwJB7RN3f3pTJHZkyABuULqbtknqezQyWadFEp5BzOXaZzRqAw2md6/d3ybxQJt+BNgpvzOg==",
"requires": {
"@types/node": "*",
"@types/tough-cookie": "*",
"form-data": "^2.5.0"
}
},
"@types/node": {
"version": "10.17.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.14.tgz",
"integrity": "sha512-G0UmX5uKEmW+ZAhmZ6PLTQ5eu/VPaT+d/tdLd5IFsKRPcbe6lPxocBtcYBFSaLaCW8O60AX90e91Nsp8lVHCNw=="
"version": "10.17.44",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.44.tgz",
"integrity": "sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw=="
},
"cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"requires": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
},
"decompress-response": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz",
"integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==",
"requires": {
"mimic-response": "^2.0.0"
}
},
"defer-to-connect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "10.5.5",
"resolved": "https://registry.npmjs.org/got/-/got-10.5.5.tgz",
"integrity": "sha512-B13HHkCkTA7KxyxTrFoZfrurBX1fZxjMTKpmIfoVzh0Xfs9aZV7xEfI6EKuERQOIPbomh5LE4xDkfK6o2VXksw==",
"requires": {
"@sindresorhus/is": "^1.0.0",
"@szmarczak/http-timer": "^4.0.0",
"@types/cacheable-request": "^6.0.1",
"cacheable-lookup": "^2.0.0",
"cacheable-request": "^7.0.1",
"decompress-response": "^5.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^5.0.0",
"lowercase-keys": "^2.0.0",
"mimic-response": "^2.0.0",
"p-cancelable": "^2.0.0",
"p-event": "^4.0.0",
"responselike": "^2.0.0",
"to-readable-stream": "^2.0.0",
"type-fest": "^0.9.0"
}
},
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
"requires": {
"json-buffer": "3.0.1"
}
},
"lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"p-cancelable": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
},
"responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"requires": {
"lowercase-keys": "^2.0.0"
}
},
"to-readable-stream": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz",
"integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w=="
},
"ts-node": {
"version": "8.6.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz",
"integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==",
"requires": {
"arg": "^4.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.6",
"yn": "3.1.1"
}
},
"type-fest": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.9.0.tgz",
"integrity": "sha512-j55pzONIdg7rdtJTRZPKIbV0FosUqYdhHK1aAYJIrUvejv1VVyBokrILE8KQDT4emW/1Ev9tx+yZG+AxuSBMmA=="
}
}
},
"@openstapps/core-tools": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@openstapps/core-tools/-/core-tools-0.16.0.tgz",
"integrity": "sha512-agcRBjavO7TlgjDvoJEnUgXzDOW5w6us2VENZ73DUC+8QKc9okdjDuVIMy2oBrKGgOxJBg0G/xKqQN9CARqqAg==",
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@openstapps/core-tools/-/core-tools-0.17.0.tgz",
"integrity": "sha512-YL0++bjhBEVb5qMHYdzCUgUb23ZGbqgkpCIHMUY5CuEZb2TUvP5HO1s1/7s/VpVVzE8lSqFqPGmeSBpHgs0K3g==",
"requires": {
"@krlwlfrt/async-pool": "0.3.0",
"@openstapps/logger": "0.5.0",
@@ -729,17 +906,6 @@
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
},
"fs-extra": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^1.0.0"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
@@ -775,22 +941,6 @@
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
},
"dependencies": {
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
}
}
},
"keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
@@ -838,33 +988,6 @@
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.9.0.tgz",
"integrity": "sha512-j55pzONIdg7rdtJTRZPKIbV0FosUqYdhHK1aAYJIrUvejv1VVyBokrILE8KQDT4emW/1Ev9tx+yZG+AxuSBMmA=="
},
"typedoc": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz",
"integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==",
"requires": {
"fs-extra": "^9.0.1",
"handlebars": "^4.7.6",
"highlight.js": "^10.0.0",
"lodash": "^4.17.15",
"lunr": "^2.3.8",
"marked": "^1.1.1",
"minimatch": "^3.0.0",
"progress": "^2.0.3",
"shelljs": "^0.8.4",
"typedoc-default-themes": "^0.10.2"
}
},
"typescript": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug=="
}
}
},
@@ -2425,9 +2548,9 @@
"dev": true
},
"core-js": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.7.0.tgz",
"integrity": "sha512-NwS7fI5M5B85EwpWuIwJN4i/fbisQUwLwiSNUWeXlkAZ0sbBjLEvLvFLf1uzAUV66PcEPt4xCGCmOZSxVf3xzA=="
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.0.tgz",
"integrity": "sha512-W2VYNB0nwQQE7tKS7HzXd7r2y/y2SVJl4ga6oH/dnaLFzM0o2lB2P3zCkWj5Wc/zyMYjtgd5Hmhk0ObkQFZOIA=="
},
"core-util-is": {
"version": "1.0.2",
@@ -3512,9 +3635,9 @@
}
},
"http-status-codes": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.2.tgz",
"integrity": "sha512-zpZ1nBcoR0j1FLQ7xbXXBy1z/yUfAi+0a5IZBoZnmOseYkaljdzQ17ZeVXFlK23IbLxMJn6aWI0uU92DQQrG0g=="
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz",
"integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg=="
},
"humanize-string": {
"version": "2.1.0",
@@ -6111,7 +6234,6 @@
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz",
"integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==",
"dev": true,
"requires": {
"fs-extra": "^9.0.1",
"handlebars": "^4.7.6",
@@ -6129,7 +6251,6 @@
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
"dev": true,
"requires": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
@@ -6141,7 +6262,6 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
@@ -6150,16 +6270,14 @@
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"dev": true
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
}
}
},
"universalify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
"dev": true
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug=="
}
}
},
@@ -6174,8 +6292,7 @@
"typescript": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
},
"tz-offset": {
"version": "0.0.1",

View File

@@ -33,8 +33,8 @@
},
"dependencies": {
"@elastic/elasticsearch": "5.6.20",
"@openstapps/core": "0.38.0",
"@openstapps/core-tools": "0.16.0",
"@openstapps/core": "0.41.0",
"@openstapps/core-tools": "0.17.0",
"@openstapps/logger": "0.5.0",
"@types/node": "10.14.12",
"commander": "2.20.0",

View File

@@ -283,6 +283,53 @@ export interface ESTermFilter {
};
}
export interface ESGenericRange<T> {
/**
* Greater than field
*/
gt?: T;
/**
* Greater or equal than field
*/
gte?: T;
/**
* Less than field
*/
lt?: T;
/**
* Less or equal than field
*/
lte?: T;
}
interface ESGenericRangeFilter<G, T extends ESGenericRange<G>> {
/**
* Range filter definition
*/
range: {
[fieldName: string]: T;
};
}
export interface ESDateRange extends ESGenericRange<string> {
/**
* Optional date format override
*/
format?: string;
/**
* Optional timezone specifier
*/
time_zone?: string;
}
export type ESNumericRangeFilter = ESGenericRangeFilter<number, ESGenericRange<number>>;
export type ESDateRangeFilter = ESGenericRangeFilter<string, ESDateRange>;
export type ESRangeFilter = ESNumericRangeFilter | ESDateRangeFilter;
/**
* Checks if the parameter is of type ESTermsFilter
* @param agg the value to check
@@ -435,9 +482,9 @@ export interface ESFunctionScoreQueryFunction {
}
/**
* An elasticsearch ducet sort
* An elasticsearch generic sort
*/
export interface ESDucetSort {
export interface ESGenericSort {
[field: string]: string;
}

View File

@@ -26,13 +26,19 @@ import {
SCThingsField,
SCThingType,
} from '@openstapps/core';
import {ElasticsearchConfig, ScriptSort} from './common';
import {
ElasticsearchConfig,
ESDateRange,
ESDateRangeFilter, ESGenericRange, ESNumericRangeFilter,
ESRangeFilter,
ScriptSort,
} from './common';
import {
ESBooleanFilter,
ESBooleanFilterArguments,
ESDucetSort,
ESFunctionScoreQuery,
ESFunctionScoreQueryFunction,
ESGenericSort,
ESGeoDistanceFilter,
ESGeoDistanceFilterArguments,
ESGeoDistanceSort,
@@ -89,7 +95,8 @@ export function buildBooleanFilter(booleanFilter: SCSearchBooleanFilter): ESBool
* Converts Array of Filters to elasticsearch query-syntax
* @param filter A search filter for the retrieval of the data
*/
export function buildFilter(filter: SCSearchFilter): ESTermFilter | ESGeoDistanceFilter | ESBooleanFilter<unknown> {
export function buildFilter(filter: SCSearchFilter):
ESTermFilter | ESGeoDistanceFilter | ESBooleanFilter<unknown> | ESRangeFilter {
switch (filter.type) {
case 'value':
@@ -109,8 +116,7 @@ export function buildFilter(filter: SCSearchFilter): ESTermFilter | ESGeoDistanc
};
} = {};
startRangeFilter[filter.arguments.fromField] = {
lte: typeof filter.arguments.time !== 'undefined'
? filter.arguments.time : 'now',
lte: filter.arguments.time ?? 'now',
};
const endRangeFilter: {
@@ -122,8 +128,7 @@ export function buildFilter(filter: SCSearchFilter): ESTermFilter | ESGeoDistanc
};
} = {};
endRangeFilter[filter.arguments.toField] = {
gte: typeof filter.arguments.time !== 'undefined'
? filter.arguments.time : 'now',
gte: filter.arguments.time ?? 'now',
};
return {
@@ -176,6 +181,42 @@ export function buildFilter(filter: SCSearchFilter): ESTermFilter | ESGeoDistanc
return {
bool: buildBooleanFilter(filter),
};
case 'numeric range':
const numericRangeObject: ESGenericRange<number> = {};
if (filter.arguments.bounds.lowerBound?.mode === 'exclusive') {
numericRangeObject.gt = filter.arguments.bounds.lowerBound.limit;
} else if (filter.arguments.bounds.lowerBound?.mode === 'inclusive') {
numericRangeObject.gte = filter.arguments.bounds.lowerBound.limit;
}
if (filter.arguments.bounds.upperBound?.mode === 'exclusive') {
numericRangeObject.lt = filter.arguments.bounds.upperBound.limit;
} else if (filter.arguments.bounds.upperBound?.mode === 'inclusive') {
numericRangeObject.lte = filter.arguments.bounds.upperBound.limit;
}
const numericRangeFilter: ESNumericRangeFilter = {range: {}};
numericRangeFilter.range[filter.arguments.field] = numericRangeObject;
return numericRangeFilter;
case 'date range':
const dateRangeObject: ESDateRange = {};
if (filter.arguments.bounds.lowerBound?.mode === 'exclusive') {
dateRangeObject.lt = filter.arguments.bounds.lowerBound.limit;
} else if (filter.arguments.bounds.lowerBound?.mode === 'inclusive') {
dateRangeObject.lte = filter.arguments.bounds.lowerBound.limit;
}
if (filter.arguments.bounds.upperBound?.mode === 'exclusive') {
dateRangeObject.gt = filter.arguments.bounds.upperBound.limit;
} else if (filter.arguments.bounds.upperBound?.mode === 'inclusive') {
dateRangeObject.gte = filter.arguments.bounds.upperBound.limit;
}
dateRangeObject.format = filter.arguments.format;
dateRangeObject.time_zone = filter.arguments.timeZone;
const dateRangeFilter: ESDateRangeFilter = {range: {}};
dateRangeFilter.range[filter.arguments.field] = dateRangeObject;
return dateRangeFilter;
}
}
@@ -403,14 +444,19 @@ export function buildQuery(
*/
export function buildSort(
sorts: SCSearchSort[],
): Array<ESDucetSort | ESGeoDistanceSort | ScriptSort> {
): Array<ESGenericSort | ESGeoDistanceSort | ScriptSort> {
return sorts.map((sort) => {
switch (sort.type) {
case 'ducet':
const ducetSort: ESDucetSort = {};
ducetSort[`${sort.arguments.field}.sort`] = sort.order;
case 'generic':
const esGenericSort: ESGenericSort = {};
esGenericSort[sort.arguments.field] = sort.order;
return ducetSort;
return esGenericSort;
case 'ducet':
const esDucetSort: ESGenericSort = {};
esDucetSort[`${sort.arguments.field}.sort`] = sort.order;
return esDucetSort;
case 'distance':
const args: ESGeoDistanceSortArguments = {
mode: 'avg',

View File

@@ -15,16 +15,18 @@
*/
import {
SCConfigFile,
SCSearchBooleanFilter,
SCSearchFilter,
SCSearchBooleanFilter, SCSearchDateRangeFilter,
SCSearchFilter, SCSearchNumericRangeFilter,
SCSearchQuery,
SCSearchSort,
SCThingType
} from '@openstapps/core';
import { expect } from 'chai';
import {expect} from 'chai';
import {ESDateRangeFilter} from '../../../src/storage/elasticsearch/common';
import {ESNumericRangeFilter} from '../../../src/storage/elasticsearch/common';
import {configFile} from '../../../src/common';
import {
ElasticsearchConfig, ESBooleanFilter, ESDucetSort, ESGeoDistanceFilter,
ElasticsearchConfig, ESBooleanFilter, ESGenericSort, ESGeoDistanceFilter,
ESGeoDistanceSort,
ESTermFilter,
ScriptSort
@@ -55,7 +57,7 @@ describe('Query', function () {
},
type: 'boolean'
};
const booleanFilters: {[key: string]: SCSearchBooleanFilter} = {
const booleanFilters: { [key: string]: SCSearchBooleanFilter } = {
and: booleanFilter,
or: {...booleanFilter, arguments: {...booleanFilter.arguments, operation: 'or'}},
not: {...booleanFilter, arguments: {...booleanFilter.arguments, operation: 'not'}},
@@ -95,34 +97,34 @@ describe('Query', function () {
describe('buildQuery', function () {
const params: SCSearchQuery = {
query: 'mathematics',
from: 30,
size: 5,
sort: [
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'name'
}
},
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'categories'
}
},
],
filter: {
type: 'value',
arguments: {
field: 'type',
value: SCThingType.AcademicEvent
}
}
query: 'mathematics',
from: 30,
size: 5,
sort: [
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'name'
}
},
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'categories'
}
},
],
filter: {
type: 'value',
arguments: {
field: 'type',
value: SCThingType.AcademicEvent
}
}
};
let esConfig: ElasticsearchConfig = {
name: 'elasticsearch',
@@ -143,7 +145,7 @@ describe('Query', function () {
fuzziness: 'AUTO',
cutoffFrequency: 0.0,
tieBreaker: 0,
}
};
const config: SCConfigFile = {
...configFile
};
@@ -187,7 +189,7 @@ describe('Query', function () {
});
describe('buildFilter', function () {
const searchFilters: {[key: string]: SCSearchFilter} = {
const searchFilters: { [key: string]: SCSearchFilter } = {
value: {
type: 'value',
arguments: {
@@ -246,6 +248,107 @@ describe('Query', function () {
expect(filter).to.be.eql(expectedFilter);
});
it('should build numeric range filters', function () {
for (const upperMode of ['inclusive', 'exclusive', null]) {
for (const lowerMode of ['inclusive', 'exclusive', null]) {
const expectedFilter: ESNumericRangeFilter = {
range: {
price: {
}
}
}
const rawFilter: SCSearchNumericRangeFilter = {
type: 'numeric range',
arguments: {
bounds: {},
field: 'price'
}
};
const setBound = (location: 'upperBound' | 'lowerBound', bound: string | null) => {
let out: number | null = null;
if (bound != null) {
out = Math.random();
rawFilter.arguments.bounds[location] = {
mode: bound as 'inclusive' | 'exclusive',
limit: out,
}
// @ts-ignore implicit any
expectedFilter.range.price[`${location === 'upperBound' ? 'g' : 'l'}${bound === 'inclusive' ? 'te' : 't'}`] = out;
}
}
setBound('upperBound', upperMode);
setBound('lowerBound', lowerMode);
const filter = buildFilter(rawFilter) as ESNumericRangeFilter;
expect(filter).to.deep.equal(expectedFilter);
for (const bound of ['g', 'l']) {
// @ts-ignore implicit any
const inclusiveExists = typeof filter.range.price[`${bound}t`] !== 'undefined';
// @ts-ignore implicit any
const exclusiveExists = typeof filter.range.price[`${bound}te`] !== 'undefined';
// only one should exist at the same time
expect(inclusiveExists && exclusiveExists).to.be.false
}
}
}
});
it('should build date range filters', function () {
for (const upperMode of ['inclusive', 'exclusive', null]) {
for (const lowerMode of ['inclusive', 'exclusive', null]) {
const expectedFilter: ESDateRangeFilter = {
range: {
price: {
format: 'thisIsADummyFormat',
time_zone: 'thisIsADummyTimeZone',
}
}
}
const rawFilter: SCSearchDateRangeFilter = {
type: 'date range',
arguments: {
bounds: {},
field: 'price',
format: 'thisIsADummyFormat',
timeZone: 'thisIsADummyTimeZone',
}
};
const setBound = (location: 'upperBound' | 'lowerBound', bound: string | null) => {
let out: string | null = null;
if (bound != null) {
out = `${location} ${bound} ${upperMode} ${lowerMode}`;
rawFilter.arguments.bounds[location] = {
mode: bound as 'inclusive' | 'exclusive',
limit: out,
}
// @ts-ignore implicit any
expectedFilter.range.price[`${location === 'upperBound' ? 'g' : 'l'}${bound === 'inclusive' ? 'te' : 't'}`] = out;
}
}
setBound('upperBound', upperMode);
setBound('lowerBound', lowerMode);
const filter = buildFilter(rawFilter) as ESNumericRangeFilter;
expect(filter).to.deep.equal(expectedFilter);
for (const bound of ['g', 'l']) {
// @ts-ignore implicit any
const inclusiveExists = typeof filter.range.price[`${bound}t`] !== 'undefined';
// @ts-ignore implicit any
const exclusiveExists = typeof filter.range.price[`${bound}te`] !== 'undefined';
// only one should exist at the same time
expect(inclusiveExists && exclusiveExists).to.be.false
}
}
}
});
it('should build availability filter', function () {
const filter = buildFilter(searchFilters.availability);
const expectedFilter: ESBooleanFilter<any> = {
@@ -366,7 +469,7 @@ describe('Query', function () {
must_not: [],
should: []
}
}
};
expect(filter).to.be.eql(expectedFilter);
});
@@ -381,6 +484,13 @@ describe('Query', function () {
field: 'name'
},
},
{
type: 'generic',
order: 'desc',
arguments: {
field: 'name',
}
},
{
type: 'distance',
order: 'desc',
@@ -398,11 +508,14 @@ describe('Query', function () {
}
},
];
let sorts: Array<ESDucetSort | ESGeoDistanceSort | ScriptSort> = [];
const expectedSorts: {[key: string]: ESDucetSort | ESGeoDistanceSort | ScriptSort} = {
let sorts: Array<ESGenericSort | ESGeoDistanceSort | ScriptSort> = [];
const expectedSorts: { [key: string]: ESGenericSort | ESGeoDistanceSort | ScriptSort } = {
ducet: {
'name.sort': 'desc'
},
generic: {
'name': 'desc'
},
distance: {
_geo_distance: {
mode: 'avg',
@@ -430,12 +543,19 @@ describe('Query', function () {
expect(sorts[0]).to.be.eql(expectedSorts.ducet);
});
it('should build generic sort', function () {
expect(sorts[1]).to.be.eql(expectedSorts.generic);
})
it('should build distance sort', function () {
expect(sorts[1]).to.be.eql(expectedSorts.distance);
expect(sorts[2]).to.be.eql(expectedSorts.distance);
});
it('should build price sort', function () {
const priceSortNoScript = {...sorts[2], _script: {...(sorts[2] as ScriptSort)._script, script: (expectedSorts.price as ScriptSort)._script.script}}
const priceSortNoScript = {
...sorts[3],
_script: {...(sorts[3] as ScriptSort)._script, script: (expectedSorts.price as ScriptSort)._script.script}
};
expect(priceSortNoScript).to.be.eql(expectedSorts.price);
});
});