moved filter autocomplete to worker
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
|
||||
const maxKeys = 11000;
|
||||
|
||||
onmessage = (e) => {
|
||||
if (!e.data.collections) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = {};
|
||||
|
||||
result.baseKeys = CommonHelper.getCollectionAutocompleteKeys(e.data.collections, e.data.baseCollection?.name);
|
||||
result.baseKeys = limitArray(result.baseKeys.sort(keysSort), maxKeys);
|
||||
|
||||
if (!e.data.disableRequestKeys) {
|
||||
result.requestKeys = CommonHelper.getRequestAutocompleteKeys(e.data.collections, e.data.baseCollection?.name);
|
||||
result.requestKeys = limitArray(result.requestKeys.sort(keysSort), maxKeys);
|
||||
}
|
||||
|
||||
if (!e.data.disableCollectionJoinKeys) {
|
||||
result.collectionJoinKeys = CommonHelper.getCollectionJoinAutocompleteKeys(e.data.collections);
|
||||
result.collectionJoinKeys = limitArray(result.collectionJoinKeys.sort(keysSort), maxKeys);
|
||||
}
|
||||
|
||||
postMessage(result);
|
||||
};
|
||||
|
||||
// sort shorter keys first
|
||||
function keysSort(a, b) {
|
||||
return a.length - b.length;
|
||||
}
|
||||
|
||||
function limitArray(arr, max) {
|
||||
if (arr.length > max) {
|
||||
return arr.slice(0, max);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { collections } from "@/stores/collections";
|
||||
import CommonHelper from "@/utils/CommonHelper";
|
||||
import AutocompleteWorker from "@/autocomplete.worker.js?worker";
|
||||
// code mirror imports
|
||||
// ---
|
||||
import {
|
||||
@@ -54,7 +55,7 @@
|
||||
StreamLanguage,
|
||||
syntaxTree,
|
||||
} from "@codemirror/language";
|
||||
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
|
||||
import { defaultKeymap, indentWithTab, history, historyKeymap } from "@codemirror/commands";
|
||||
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
||||
import {
|
||||
autocompletion,
|
||||
@@ -75,7 +76,7 @@
|
||||
export let singleLine = false;
|
||||
export let extraAutocompleteKeys = []; // eg. ["test1", "test2"]
|
||||
export let disableRequestKeys = false;
|
||||
export let disableIndirectCollectionsKeys = false;
|
||||
export let disableCollectionJoinKeys = false;
|
||||
|
||||
let editor;
|
||||
let container;
|
||||
@@ -84,10 +85,10 @@
|
||||
let editableCompartment = new Compartment();
|
||||
let readOnlyCompartment = new Compartment();
|
||||
let placeholderCompartment = new Compartment();
|
||||
let autocompleteWorker = new AutocompleteWorker();
|
||||
|
||||
let cachedCollections = [];
|
||||
let cachedRequestKeys = [];
|
||||
let cachedIndirectCollectionKeys = [];
|
||||
let cachedCollectionJoinKeys = [];
|
||||
let cachedBaseKeys = [];
|
||||
let baseKeysChangeHash = "";
|
||||
let oldBaseKeysChangeHash = "";
|
||||
@@ -98,7 +99,7 @@
|
||||
!disabled &&
|
||||
(oldBaseKeysChangeHash != baseKeysChangeHash ||
|
||||
disableRequestKeys !== -1 ||
|
||||
disableIndirectCollectionsKeys !== -1)
|
||||
disableCollectionJoinKeys !== -1)
|
||||
) {
|
||||
oldBaseKeysChangeHash = baseKeysChangeHash;
|
||||
refreshCachedKeys();
|
||||
@@ -146,18 +147,28 @@
|
||||
editor?.focus();
|
||||
}
|
||||
|
||||
// Refresh the cached autocomplete keys.
|
||||
// ---
|
||||
let refreshDebounceId = null;
|
||||
|
||||
// Refresh the cached autocomplete keys.
|
||||
autocompleteWorker.onmessage = (e) => {
|
||||
cachedBaseKeys = e.data.baseKeys || [];
|
||||
cachedRequestKeys = e.data.requestKeys || [];
|
||||
cachedCollectionJoinKeys = e.data.collectionJoinKeys || [];
|
||||
};
|
||||
|
||||
function refreshCachedKeys() {
|
||||
clearTimeout(refreshDebounceId);
|
||||
refreshDebounceId = setTimeout(() => {
|
||||
cachedCollections = concatWithBaseCollection($collections);
|
||||
cachedBaseKeys = getBaseKeys();
|
||||
cachedRequestKeys = !disableRequestKeys ? getRequestKeys() : [];
|
||||
cachedIndirectCollectionKeys = !disableIndirectCollectionsKeys ? getIndirectCollectionKeys() : [];
|
||||
}, 300);
|
||||
autocompleteWorker.postMessage({
|
||||
baseCollection: baseCollection,
|
||||
collections: concatWithBaseCollection($collections),
|
||||
disableRequestKeys: disableRequestKeys,
|
||||
disableCollectionJoinKeys: disableCollectionJoinKeys,
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
// ---
|
||||
|
||||
// Return a collection keys hash string that can be used to compare with previous states.
|
||||
function getCollectionKeysChangeHash(collection) {
|
||||
@@ -211,129 +222,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a list with all collection field keys recursively.
|
||||
function getCollectionFieldKeys(nameOrId, prefix = "", level = 0) {
|
||||
let collection = cachedCollections.find((item) => item.name == nameOrId || item.id == nameOrId);
|
||||
if (!collection || level >= 4) {
|
||||
return [];
|
||||
}
|
||||
collection.schema = collection.schema || [];
|
||||
|
||||
let result = CommonHelper.getAllCollectionIdentifiers(collection, prefix);
|
||||
|
||||
for (const field of collection.schema) {
|
||||
const key = prefix + field.name;
|
||||
|
||||
// add relation fields
|
||||
if (field.type === "relation" && field.options?.collectionId) {
|
||||
const subKeys = getCollectionFieldKeys(field.options.collectionId, key + ".", level + 1);
|
||||
if (subKeys.length) {
|
||||
result = result.concat(subKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// add ":each" field modifier
|
||||
if (field.type === "select" && field.options?.maxSelect != 1) {
|
||||
result.push(key + ":each");
|
||||
}
|
||||
|
||||
// add ":length" field modifier to arrayble fields
|
||||
if (field.options?.maxSelect != 1 && ["select", "file", "relation"].includes(field.type)) {
|
||||
result.push(key + ":length");
|
||||
}
|
||||
}
|
||||
|
||||
// add back relations
|
||||
for (const ref of cachedCollections) {
|
||||
ref.schema = ref.schema || [];
|
||||
for (const field of ref.schema) {
|
||||
if (field.type == "relation" && field.options?.collectionId == collection.id) {
|
||||
const key = prefix + ref.name + "_via_" + field.name;
|
||||
const subKeys = getCollectionFieldKeys(ref.id, key + ".", level + 2); // +2 to reduce the recursive results
|
||||
if (subKeys.length) {
|
||||
result = result.concat(subKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns baseCollection keys.
|
||||
function getBaseKeys() {
|
||||
return getCollectionFieldKeys(baseCollection?.name);
|
||||
}
|
||||
|
||||
// Returns @request.* keys.
|
||||
function getRequestKeys() {
|
||||
const result = [];
|
||||
|
||||
result.push("@request.context");
|
||||
result.push("@request.method");
|
||||
result.push("@request.query.");
|
||||
result.push("@request.data.");
|
||||
result.push("@request.headers.");
|
||||
result.push("@request.auth.id");
|
||||
result.push("@request.auth.collectionId");
|
||||
result.push("@request.auth.collectionName");
|
||||
result.push("@request.auth.verified");
|
||||
result.push("@request.auth.username");
|
||||
result.push("@request.auth.email");
|
||||
result.push("@request.auth.emailVisibility");
|
||||
result.push("@request.auth.created");
|
||||
result.push("@request.auth.updated");
|
||||
|
||||
// load auth collection fields
|
||||
const authCollections = cachedCollections.filter((collection) => collection.type === "auth");
|
||||
for (const collection of authCollections) {
|
||||
const authKeys = getCollectionFieldKeys(collection.id, "@request.auth.");
|
||||
for (const k of authKeys) {
|
||||
CommonHelper.pushUnique(result, k);
|
||||
}
|
||||
}
|
||||
|
||||
// load base collection fields into @request.data.*
|
||||
const issetExcludeList = ["created", "updated"];
|
||||
if (baseCollection?.id) {
|
||||
const keys = getCollectionFieldKeys(baseCollection.name, "@request.data.");
|
||||
for (const key of keys) {
|
||||
result.push(key);
|
||||
|
||||
// add ":isset" modifier to non-base keys
|
||||
const parts = key.split(".");
|
||||
if (
|
||||
parts.length === 3 &&
|
||||
// doesn't contain another modifier
|
||||
parts[2].indexOf(":") === -1 &&
|
||||
// is not from the exclude list
|
||||
!issetExcludeList.includes(parts[2])
|
||||
) {
|
||||
result.push(key + ":isset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns @collection.* keys.
|
||||
function getIndirectCollectionKeys() {
|
||||
const result = [];
|
||||
|
||||
for (const collection of cachedCollections) {
|
||||
const prefix = "@collection." + collection.name + ".";
|
||||
const keys = getCollectionFieldKeys(collection.name, prefix);
|
||||
for (const key of keys) {
|
||||
result.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns an array with all the supported keys.
|
||||
function getAllKeys(includeRequestKeys = true, includeIndirectCollectionsKeys = true) {
|
||||
function getAllKeys(includeRequestKeys = true, includeCollectionJoinKeys = true) {
|
||||
let result = [].concat(extraAutocompleteKeys);
|
||||
|
||||
// add base keys
|
||||
@@ -344,16 +234,11 @@
|
||||
result = result.concat(cachedRequestKeys || []);
|
||||
}
|
||||
|
||||
// add @collections.* keys
|
||||
if (includeIndirectCollectionsKeys) {
|
||||
result = result.concat(cachedIndirectCollectionKeys || []);
|
||||
// add @collection.* keys
|
||||
if (includeCollectionJoinKeys) {
|
||||
result = result.concat(cachedCollectionJoinKeys || []);
|
||||
}
|
||||
|
||||
// sort shorter keys first
|
||||
result.sort(function (a, b) {
|
||||
return a.length - b.length;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -389,15 +274,20 @@
|
||||
{ label: "@yearEnd" },
|
||||
];
|
||||
|
||||
if (!disableIndirectCollectionsKeys) {
|
||||
if (!disableCollectionJoinKeys) {
|
||||
options.push({ label: "@collection.*", apply: "@collection." });
|
||||
}
|
||||
|
||||
const keys = getAllKeys(!disableRequestKeys, !disableRequestKeys && word.text.startsWith("@c"));
|
||||
let keys = getAllKeys(
|
||||
!disableRequestKeys && word.text.startsWith("@r"),
|
||||
!disableCollectionJoinKeys && word.text.startsWith("@c"),
|
||||
);
|
||||
|
||||
for (const key of keys) {
|
||||
options.push({
|
||||
label: key.endsWith(".") ? key + "*" : key,
|
||||
apply: key,
|
||||
boost: key.indexOf("_via_") > 0 ? -1 : 0, // deprioritize _via_ keys
|
||||
});
|
||||
}
|
||||
|
||||
@@ -498,6 +388,7 @@
|
||||
searchKeymap.find((item) => item.key === "Mod-d"),
|
||||
...historyKeymap,
|
||||
...completionKeymap,
|
||||
indentWithTab,
|
||||
]),
|
||||
EditorView.lineWrapping,
|
||||
autocompletion({
|
||||
@@ -533,6 +424,7 @@
|
||||
clearTimeout(refreshDebounceId);
|
||||
removeLabelListeners();
|
||||
editor?.destroy();
|
||||
autocompleteWorker.terminate();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
id={uniqueId}
|
||||
singleLine
|
||||
disableRequestKeys
|
||||
disableIndirectCollectionsKeys
|
||||
disableCollectionJoinKeys
|
||||
{extraAutocompleteKeys}
|
||||
baseCollection={autocompleteCollection}
|
||||
placeholder={value || placeholder}
|
||||
|
||||
@@ -1745,6 +1745,142 @@ export default class CommonHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates recursively a list with all the autocomplete field keys
|
||||
* for the collectionNameOrId collection.
|
||||
*
|
||||
* @param {Array} collections
|
||||
* @param {String} collectionNameOrId
|
||||
* @param {String} [prefix]
|
||||
* @param {Number} [level]
|
||||
* @return {Array}
|
||||
*/
|
||||
static getCollectionAutocompleteKeys(collections, collectionNameOrId, prefix = "", level = 0) {
|
||||
let collection = collections.find((item) => item.name == collectionNameOrId || item.id == collectionNameOrId);
|
||||
if (!collection || level >= 4) {
|
||||
return [];
|
||||
}
|
||||
collection.schema = collection.schema || [];
|
||||
|
||||
let result = CommonHelper.getAllCollectionIdentifiers(collection, prefix);
|
||||
|
||||
for (const field of collection.schema) {
|
||||
const key = prefix + field.name;
|
||||
|
||||
// add relation fields
|
||||
if (field.type == "relation" && field.options?.collectionId) {
|
||||
const subKeys = CommonHelper.getCollectionAutocompleteKeys(collections, field.options.collectionId, key + ".", level + 1);
|
||||
if (subKeys.length) {
|
||||
result = result.concat(subKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// add ":each" field modifier
|
||||
if (field.type == "select" && field.options?.maxSelect != 1) {
|
||||
result.push(key + ":each");
|
||||
}
|
||||
|
||||
// add ":length" field modifier to arrayble fields
|
||||
if (field.options?.maxSelect != 1 && ["select", "file", "relation"].includes(field.type)) {
|
||||
result.push(key + ":length");
|
||||
}
|
||||
}
|
||||
|
||||
// add back relations
|
||||
for (const ref of collections) {
|
||||
ref.schema = ref.schema || [];
|
||||
for (const field of ref.schema) {
|
||||
if (field.type == "relation" && field.options?.collectionId == collection.id) {
|
||||
const key = prefix + ref.name + "_via_" + field.name;
|
||||
const subKeys = CommonHelper.getCollectionAutocompleteKeys(collections, ref.id, key + ".", level + 2); // +2 to reduce the recursive results
|
||||
if (subKeys.length) {
|
||||
result = result.concat(subKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list with all @collection.* autocomplete field keys.
|
||||
*
|
||||
* @param {Array} collections
|
||||
* @return {Array}
|
||||
*/
|
||||
static getCollectionJoinAutocompleteKeys(collections) {
|
||||
const result = [];
|
||||
|
||||
for (const collection of collections) {
|
||||
const prefix = "@collection." + collection.name + ".";
|
||||
const keys = CommonHelper.getCollectionAutocompleteKeys(collections, collection.name, prefix);
|
||||
for (const key of keys) {
|
||||
result.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list with all @request.* autocomplete field keys.
|
||||
*
|
||||
* @param {Array} collections
|
||||
* @param {String} baseCollectionName (used for the `@request.data.*` fields)
|
||||
* @return {Array}
|
||||
*/
|
||||
static getRequestAutocompleteKeys(collections, baseCollectionName) {
|
||||
const result = [];
|
||||
|
||||
result.push("@request.context");
|
||||
result.push("@request.method");
|
||||
result.push("@request.query.");
|
||||
result.push("@request.data.");
|
||||
result.push("@request.headers.");
|
||||
result.push("@request.auth.id");
|
||||
result.push("@request.auth.collectionId");
|
||||
result.push("@request.auth.collectionName");
|
||||
result.push("@request.auth.verified");
|
||||
result.push("@request.auth.username");
|
||||
result.push("@request.auth.email");
|
||||
result.push("@request.auth.emailVisibility");
|
||||
result.push("@request.auth.created");
|
||||
result.push("@request.auth.updated");
|
||||
|
||||
// load auth collection fields
|
||||
const authCollections = collections.filter((collection) => collection.type === "auth");
|
||||
for (const collection of authCollections) {
|
||||
const authKeys = CommonHelper.getCollectionAutocompleteKeys(collections, collection.id, "@request.auth.");
|
||||
for (const k of authKeys) {
|
||||
CommonHelper.pushUnique(result, k);
|
||||
}
|
||||
}
|
||||
|
||||
// load base collection fields into @request.data.*
|
||||
if (baseCollectionName) {
|
||||
const issetExcludeList = ["created", "updated"];
|
||||
const keys = CommonHelper.getCollectionAutocompleteKeys(collections, baseCollectionName, "@request.data.");
|
||||
for (const key of keys) {
|
||||
result.push(key);
|
||||
|
||||
// add ":isset" modifier to non-base keys
|
||||
const parts = key.split(".");
|
||||
if (
|
||||
parts.length === 3 &&
|
||||
// doesn't contain another modifier
|
||||
parts[2].indexOf(":") === -1 &&
|
||||
// is not from the exclude list
|
||||
!issetExcludeList.includes(parts[2])
|
||||
) {
|
||||
result.push(key + ":isset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified SQL index and returns an object with its components.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user