mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 15:16:00 +00:00
search settings
This commit is contained in:
@@ -2,9 +2,9 @@ import _compact from 'lodash/compact';
|
|||||||
import _isString from 'lodash/isString';
|
import _isString from 'lodash/isString';
|
||||||
import _startCase from 'lodash/startCase';
|
import _startCase from 'lodash/startCase';
|
||||||
|
|
||||||
export interface FilterNameDefinition {
|
// export interface FilterNameDefinition {
|
||||||
childName: string;
|
// childName: string;
|
||||||
}
|
// }
|
||||||
|
|
||||||
function camelMatch(filter: string, text: string): boolean {
|
function camelMatch(filter: string, text: string): boolean {
|
||||||
if (!text) return false;
|
if (!text) return false;
|
||||||
@@ -20,7 +20,7 @@ function camelMatch(filter: string, text: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterName(filter: string, ...names: (string | FilterNameDefinition)[]) {
|
export function filterName(filter: string, ...names: string[]) {
|
||||||
if (!filter) return true;
|
if (!filter) return true;
|
||||||
|
|
||||||
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
|
||||||
@@ -28,22 +28,32 @@ export function filterName(filter: string, ...names: (string | FilterNameDefinit
|
|||||||
|
|
||||||
const namesCompacted = _compact(names);
|
const namesCompacted = _compact(names);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const namesOwn: string[] = namesCompacted.filter(x => _isString(x));
|
|
||||||
// @ts-ignore
|
|
||||||
const namesChild: string[] = namesCompacted.filter(x => x.childName).map(x => x.childName);
|
|
||||||
|
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
// const tokenUpper = token.toUpperCase();
|
const found = namesCompacted.find(name => camelMatch(token, name));
|
||||||
if (token.startsWith('#')) {
|
if (!found) return false;
|
||||||
// const tokenUpperSub = tokenUpper.substring(1);
|
|
||||||
const found = namesChild.find(name => camelMatch(token.substring(1), name));
|
|
||||||
if (!found) return false;
|
|
||||||
} else {
|
|
||||||
const found = namesOwn.find(name => camelMatch(token, name));
|
|
||||||
if (!found) return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tokenizeBySearchFilter(text: string, filter: string): { token: string; isMatch: boolean }[] {
|
||||||
|
const tokens = filter.split(' ').map(x => x.trim());
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
let lastMatch = 0;
|
||||||
|
for (const token of tokens) {
|
||||||
|
const index = text.indexOf(token, lastMatch);
|
||||||
|
if (index < 0) {
|
||||||
|
result.push({ token, isMatch: false });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({ token: text.substring(lastMatch, index), isMatch: false });
|
||||||
|
result.push({ token: text.substring(index, index + token.length), isMatch: true });
|
||||||
|
lastMatch = index + token.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({ token: text.substring(lastMatch), isMatch: false });
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
export let showPinnedInsteadOfUnpin = false;
|
export let showPinnedInsteadOfUnpin = false;
|
||||||
export let indentLevel = 0;
|
export let indentLevel = 0;
|
||||||
export let disableBoldScroll = false;
|
export let disableBoldScroll = false;
|
||||||
|
export let filter = null;
|
||||||
|
|
||||||
$: isChecked =
|
$: isChecked =
|
||||||
checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x));
|
checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x));
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
$: filtered = !groupFunc
|
$: filtered = !groupFunc
|
||||||
? list.filter(data => {
|
? list.filter(data => {
|
||||||
const matcher = module.createMatcher && module.createMatcher(data);
|
const matcher = module.createMatcher && module.createMatcher(data, passProps?.searchSettings);
|
||||||
if (matcher && !matcher(filter)) return false;
|
if (matcher && !matcher(filter)) return false;
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
$: childrenMatched = !groupFunc
|
$: childrenMatched = !groupFunc
|
||||||
? list.filter(data => {
|
? list.filter(data => {
|
||||||
const matcher = module.createChildMatcher && module.createChildMatcher(data);
|
const matcher = module.createChildMatcher && module.createChildMatcher(data, passProps?.searchSettings);
|
||||||
if (matcher && !matcher(filter)) return false;
|
if (matcher && !matcher(filter)) return false;
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
$: listGrouped = groupFunc
|
$: listGrouped = groupFunc
|
||||||
? _.compact(
|
? _.compact(
|
||||||
(list || []).map(data => {
|
(list || []).map(data => {
|
||||||
const matcher = module.createMatcher && module.createMatcher(data);
|
const matcher = module.createMatcher && module.createMatcher(data, passProps?.searchSettings);
|
||||||
const isMatched = matcher && !matcher(filter) ? false : true;
|
const isMatched = matcher && !matcher(filter) ? false : true;
|
||||||
const group = groupFunc(data);
|
const group = groupFunc(data);
|
||||||
return { group, data, isMatched };
|
return { group, data, isMatched };
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
{module}
|
{module}
|
||||||
{disableContextMenu}
|
{disableContextMenu}
|
||||||
{passProps}
|
{passProps}
|
||||||
|
{filter}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
|
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
|
||||||
|
|||||||
@@ -3,14 +3,30 @@
|
|||||||
|
|
||||||
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||||
export const createMatcher =
|
export const createMatcher =
|
||||||
({ schemaName, pureName, columns }) =>
|
(
|
||||||
filter =>
|
{ schemaName, pureName, objectComment, tableEngine, columns, objectTypeField, createSql },
|
||||||
filterName(
|
cfg = DEFAULT_SEARCH_SETTINGS
|
||||||
filter,
|
) =>
|
||||||
pureName,
|
filter => {
|
||||||
schemaName,
|
const filterArgs = [];
|
||||||
...(columns?.map(({ columnName }) => ({ childName: columnName })) || [])
|
if (cfg.schemaName) filterArgs.push(schemaName);
|
||||||
);
|
if (objectTypeField == 'tables') {
|
||||||
|
if (cfg.tableName) filterArgs.push(pureName);
|
||||||
|
if (cfg.tableComment) filterArgs.push(objectComment);
|
||||||
|
if (cfg.tableEngine) filterArgs.push(tableEngine);
|
||||||
|
|
||||||
|
for (const column of columns || []) {
|
||||||
|
if (cfg.columnName) filterArgs.push(column.columnName);
|
||||||
|
if (cfg.columnComment) filterArgs.push(column.columnComment);
|
||||||
|
if (cfg.columnDataType) filterArgs.push(column.dataType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cfg.sqlObjectName) filterArgs.push(pureName);
|
||||||
|
if (cfg.sqlObjectText) filterArgs.push(createSql);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterName(filter, ...filterArgs);
|
||||||
|
};
|
||||||
export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||||
|
|
||||||
export const databaseObjectIcons = {
|
export const databaseObjectIcons = {
|
||||||
@@ -877,9 +893,11 @@
|
|||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
|
DEFAULT_SEARCH_SETTINGS,
|
||||||
extensions,
|
extensions,
|
||||||
getActiveTab,
|
getActiveTab,
|
||||||
getCurrentSettings,
|
getCurrentSettings,
|
||||||
|
getDatabaseObjectAppObjectSearchSettings,
|
||||||
getExtensions,
|
getExtensions,
|
||||||
getLastUsedDefaultActions,
|
getLastUsedDefaultActions,
|
||||||
lastUsedDefaultActions,
|
lastUsedDefaultActions,
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
let submenuItem;
|
let submenuItem;
|
||||||
let submenuOffset;
|
let submenuOffset;
|
||||||
|
|
||||||
|
let switchIndex = 0;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
let closeHandlers = [];
|
let closeHandlers = [];
|
||||||
|
|
||||||
@@ -80,6 +82,14 @@
|
|||||||
submenuOffset = hoverOffset;
|
submenuOffset = hoverOffset;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (item.switchStore) {
|
||||||
|
item.switchStore.update(x => ({
|
||||||
|
...x,
|
||||||
|
[item.switchValue]: !x[item.switchValue],
|
||||||
|
}));
|
||||||
|
switchIndex++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
dispatchClose();
|
dispatchClose();
|
||||||
if (onCloseParent) onCloseParent();
|
if (onCloseParent) onCloseParent();
|
||||||
if (item.onClick) item.onClick();
|
if (item.onClick) item.onClick();
|
||||||
@@ -131,7 +141,18 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a on:click={e => handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}>
|
<a on:click={e => handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}>
|
||||||
{item.text || item.label}
|
<span>
|
||||||
|
{#if item.switchStoreGetter}
|
||||||
|
{#key switchIndex}
|
||||||
|
{#if item.switchStoreGetter()[item.switchValue]}
|
||||||
|
<FontIcon icon="icon checkbox-marked" padRight />
|
||||||
|
{:else}
|
||||||
|
<FontIcon icon="icon checkbox-blank" padRight />
|
||||||
|
{/if}
|
||||||
|
{/key}
|
||||||
|
{/if}
|
||||||
|
{item.text || item.label}
|
||||||
|
</span>
|
||||||
{#if item.keyText}
|
{#if item.keyText}
|
||||||
<span class="keyText">{formatKeyText(item.keyText)}</span>
|
<span class="keyText">{formatKeyText(item.keyText)}</span>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -179,6 +200,7 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: calc(100% - 20px);
|
max-height: calc(100% - 20px);
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyText {
|
.keyText {
|
||||||
|
|||||||
@@ -161,6 +161,25 @@ export const lastUsedDefaultActions = writableWithStorage({}, 'lastUsedDefaultAc
|
|||||||
export const selectedDatabaseObjectAppObject = writable(null);
|
export const selectedDatabaseObjectAppObject = writable(null);
|
||||||
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
||||||
|
|
||||||
|
export const DEFAULT_SEARCH_SETTINGS = {
|
||||||
|
collectionName: true,
|
||||||
|
schemaName: false,
|
||||||
|
tableName: true,
|
||||||
|
viewName: true,
|
||||||
|
columnName: true,
|
||||||
|
columnDataType: true,
|
||||||
|
tableComment: true,
|
||||||
|
columnComment: true,
|
||||||
|
sqlObjectName: true,
|
||||||
|
sqlObjectText: true,
|
||||||
|
tableEngine: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const databaseObjectAppObjectSearchSettings = writableWithStorage(
|
||||||
|
DEFAULT_SEARCH_SETTINGS,
|
||||||
|
'databaseObjectAppObjectSearchSettings'
|
||||||
|
);
|
||||||
|
|
||||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||||
);
|
);
|
||||||
@@ -359,3 +378,11 @@ lastUsedDefaultActions.subscribe(value => {
|
|||||||
lastUsedDefaultActionsValue = value;
|
lastUsedDefaultActionsValue = value;
|
||||||
});
|
});
|
||||||
export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue;
|
export const getLastUsedDefaultActions = () => lastUsedDefaultActionsValue;
|
||||||
|
|
||||||
|
let databaseObjectAppObjectSearchSettingsValue: typeof DEFAULT_SEARCH_SETTINGS = {
|
||||||
|
...DEFAULT_SEARCH_SETTINGS,
|
||||||
|
};
|
||||||
|
databaseObjectAppObjectSearchSettings.subscribe(value => {
|
||||||
|
databaseObjectAppObjectSearchSettingsValue = value;
|
||||||
|
});
|
||||||
|
export const getDatabaseObjectAppObjectSearchSettings = () => databaseObjectAppObjectSearchSettingsValue;
|
||||||
|
|||||||
@@ -38,8 +38,10 @@
|
|||||||
import { extractDbNameFromComposite, findEngineDriver } from 'dbgate-tools';
|
import { extractDbNameFromComposite, findEngineDriver } from 'dbgate-tools';
|
||||||
import {
|
import {
|
||||||
currentDatabase,
|
currentDatabase,
|
||||||
|
databaseObjectAppObjectSearchSettings,
|
||||||
extensions,
|
extensions,
|
||||||
focusedConnectionOrDatabase,
|
focusedConnectionOrDatabase,
|
||||||
|
getDatabaseObjectAppObjectSearchSettings,
|
||||||
getSelectedDatabaseObjectAppObject,
|
getSelectedDatabaseObjectAppObject,
|
||||||
selectedDatabaseObjectAppObject,
|
selectedDatabaseObjectAppObject,
|
||||||
} from '../stores';
|
} from '../stores';
|
||||||
@@ -124,8 +126,32 @@
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSearchMenu() {
|
||||||
|
const res = [];
|
||||||
|
if (driver?.databaseEngineTypes?.includes('document')) {
|
||||||
|
res.push({ label: 'Collection names' });
|
||||||
|
}
|
||||||
|
if (driver?.databaseEngineTypes?.includes('sql')) {
|
||||||
|
res.push({ label: 'Schema name', switchValue: 'schemaName' });
|
||||||
|
res.push({ label: 'Table name', switchValue: 'tableName' });
|
||||||
|
res.push({ label: 'View name', switchValue: 'viewName' });
|
||||||
|
res.push({ label: 'Column name', switchValue: 'columnName' });
|
||||||
|
res.push({ label: 'Column data type', switchValue: 'columnType' });
|
||||||
|
res.push({ label: 'Table comment', switchValue: 'tableComment' });
|
||||||
|
res.push({ label: 'Column comment', switchValue: 'columnComment' });
|
||||||
|
res.push({ label: 'Procedure/function/trigger name', switchValue: 'sqlObjectName' });
|
||||||
|
res.push({ label: 'Procedure/function/trigger text', switchValue: 'sqlObjectText' });
|
||||||
|
res.push({ label: 'Table engine', switchValue: 'tableEngine' });
|
||||||
|
}
|
||||||
|
return res.map(item => ({
|
||||||
|
...item,
|
||||||
|
switchStore: databaseObjectAppObjectSearchSettings,
|
||||||
|
switchStoreGetter: getDatabaseObjectAppObjectSearchSettings,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
$: flatFilteredList = objectList.filter(data => {
|
$: flatFilteredList = objectList.filter(data => {
|
||||||
const matcher = databaseObjectAppObject.createMatcher(data);
|
const matcher = databaseObjectAppObject.createMatcher(data, $databaseObjectAppObjectSearchSettings);
|
||||||
if (matcher && !matcher(filter)) return false;
|
if (matcher && !matcher(filter)) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@@ -184,7 +210,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<SearchBoxWrapper>
|
<SearchBoxWrapper>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
placeholder="Search in tables, objects, # prefix in columns"
|
placeholder="Search in tables, views, procedures"
|
||||||
bind:value={filter}
|
bind:value={filter}
|
||||||
bind:this={domFilter}
|
bind:this={domFilter}
|
||||||
onFocusFilteredList={() => {
|
onFocusFilteredList={() => {
|
||||||
@@ -192,7 +218,12 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CloseSearchButton bind:filter />
|
<CloseSearchButton bind:filter />
|
||||||
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
{#if filter}
|
||||||
|
<DropDownButton icon="icon filter" menu={createSearchMenu} />
|
||||||
|
{/if}
|
||||||
|
{#if !filter}
|
||||||
|
<DropDownButton icon="icon plus-thick" menu={createAddMenu} />
|
||||||
|
{/if}
|
||||||
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
<InlineButton on:click={handleRefreshDatabase} title="Refresh database connection and object list" square>
|
||||||
<FontIcon icon="icon refresh" />
|
<FontIcon icon="icon refresh" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
@@ -256,6 +287,7 @@
|
|||||||
showPinnedInsteadOfUnpin: true,
|
showPinnedInsteadOfUnpin: true,
|
||||||
connection: $connection,
|
connection: $connection,
|
||||||
hideSchemaName: !!$appliedCurrentSchema,
|
hideSchemaName: !!$appliedCurrentSchema,
|
||||||
|
searchSettings: $databaseObjectAppObjectSearchSettings,
|
||||||
}}
|
}}
|
||||||
getIsExpanded={data =>
|
getIsExpanded={data =>
|
||||||
expandedObjects.includes(`${data.objectTypeField}||${data.schemaName}||${data.pureName}`)}
|
expandedObjects.includes(`${data.objectTypeField}||${data.schemaName}||${data.pureName}`)}
|
||||||
|
|||||||
Reference in New Issue
Block a user