Skip to content

Commit

Permalink
fix: search relative (#1102)
Browse files Browse the repository at this point in the history
* perf: add search hightlight contrast

* perf: update search option display

* fix: search error when table field search cache does not target

* test: add formula_boolean field to search test

* chore: delete useless code

* fix: exchange search mode should reset filter text

* fix: change search filter option do not update row count
  • Loading branch information
caoxing9 authored Nov 21, 2024
1 parent de4ef9d commit d4b0b96
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 65 deletions.
46 changes: 22 additions & 24 deletions apps/nestjs-backend/src/db-provider/search-query/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,39 +172,37 @@ export abstract class SearchQueryAbstract {
searchField: IFieldInstance[],
searchValue: string
) {
const searchQuery = searchField
.map((field) => {
const searchQueryBuilder = new SearchQuery(queryBuilder, field, searchValue);

if (field.isMultipleCellValue) {
switch (field.cellValueType) {
case CellValueType.DateTime:
return searchQueryBuilder.getMultipleDateSqlQuery();
case CellValueType.Number:
return searchQueryBuilder.getMultipleNumberSqlQuery();
case CellValueType.String:
if (field.isStructuredCellValue) {
return searchQueryBuilder.getMultipleJsonSqlQuery();
} else {
return searchQueryBuilder.getMultipleTextSqlQuery();
}
}
}
const searchQuery = searchField.map((field) => {
const searchQueryBuilder = new SearchQuery(queryBuilder, field, searchValue);

if (field.isMultipleCellValue) {
switch (field.cellValueType) {
case CellValueType.DateTime:
return searchQueryBuilder.getDateSqlQuery();
return searchQueryBuilder.getMultipleDateSqlQuery();
case CellValueType.Number:
return searchQueryBuilder.getNumberSqlQuery();
return searchQueryBuilder.getMultipleNumberSqlQuery();
case CellValueType.String:
if (field.isStructuredCellValue) {
return searchQueryBuilder.getJsonSqlQuery();
return searchQueryBuilder.getMultipleJsonSqlQuery();
} else {
return searchQueryBuilder.getTextSqlQuery();
return searchQueryBuilder.getMultipleTextSqlQuery();
}
}
})
.filter((sql) => !!sql);
}

switch (field.cellValueType) {
case CellValueType.DateTime:
return searchQueryBuilder.getDateSqlQuery();
case CellValueType.Number:
return searchQueryBuilder.getNumberSqlQuery();
case CellValueType.String:
if (field.isStructuredCellValue) {
return searchQueryBuilder.getJsonSqlQuery();
} else {
return searchQueryBuilder.getTextSqlQuery();
}
}
});

const knexInstance = queryBuilder.client;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,10 @@ export class AggregationService {
projection
);

if (searchFields?.length === 0) {
return { count: 0 };
}

const queryBuilder = this.knex(dbFieldName);
this.dbProvider.searchCountQuery(queryBuilder, searchFields, search[0]);
this.dbProvider
Expand Down Expand Up @@ -641,6 +645,10 @@ export class AggregationService {
projection
);

if (searchFields.length === 0) {
return null;
}

const { queryBuilder: viewRecordsQB } = await this.recordService.buildFilterSortQuery(
tableId,
queryRo
Expand Down
8 changes: 7 additions & 1 deletion apps/nestjs-backend/src/features/record/record.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
import {
and,
CellFormat,
DbFieldType,
FieldKeyType,
FieldType,
generateRecordId,
Expand Down Expand Up @@ -1421,7 +1422,7 @@ export class RecordService {
return searchArr.includes(field.id);
})
.filter((field) => {
if (field.type === FieldType.Checkbox) {
if (field.dbFieldType === DbFieldType.Boolean) {
return false;
}
return true;
Expand Down Expand Up @@ -1460,6 +1461,11 @@ export class RecordService {
{} as Record<string, IFieldInstance>
);
const searchFields = await this.getSearchFields(fieldInstanceMap, search, viewId, projection);

if (searchFields.length === 0) {
return null;
}

const newQuery = this.knex
.with('current_page_records', (qb) => {
qb.select('*').from(dbTableName).whereIn('__id', Ids);
Expand Down
9 changes: 9 additions & 0 deletions apps/nestjs-backend/test/aggregation-search.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { INestApplication } from '@nestjs/common';
import { FieldType } from '@teable/core';
import type { ITableFullVo } from '@teable/openapi';
import { getSearchCount, getSearchIndex, createField, updateViewColumnMeta } from '@teable/openapi';
import { x_20 } from './data-helpers/20x';
Expand Down Expand Up @@ -37,6 +38,14 @@ describe('OpenAPI AggregationController (e2e)', () => {
for (const field of x20LinkFromLookups.fields) {
await createField(subTable.id, field);
}

await createField(table.id, {
name: 'Formula_Boolean',
options: {
expression: `{${table.fields[0].id}} > 1`,
},
type: FieldType.Formula,
});
});

afterAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ export const SearchButton = (props: ISearchButtonProps) => {
const filteredFields = selectedField.filter(
(f) => !hiddenFields.includes(f) && fields.map((f) => f.id).includes(f)
);
const primaryFieldId = fields.find((f) => f.isPrimary)?.id;
const defaultFieldId = fields?.[0]?.id;
if (!isEqual(filteredFields, selectedField)) {
tableId &&
setSearchFieldMap({
...searchFieldMapCache,
[tableId]: filteredFields,
[tableId]: filteredFields?.length ? filteredFields : [defaultFieldId],
});
setFieldId(filteredFields.length > 0 ? filteredFields.join(',') : primaryFieldId);
setFieldId(filteredFields.length > 0 ? filteredFields.join(',') : defaultFieldId);
}
}, [
fieldId,
Expand Down Expand Up @@ -200,7 +200,9 @@ export const SearchButton = (props: ISearchButtonProps) => {
onChange={(fieldIds) => {
// switch to field
if (!fieldIds || fields.length === 0) {
const newIds = searchFieldMapCache?.[tableId] || [fields[0].id];
const newIds = searchFieldMapCache?.[tableId]?.length
? searchFieldMapCache?.[tableId]
: [fields?.[0].id];
setFieldId(newIds.join(','));
setEnableGlobalSearch(false);
return;
Expand Down
111 changes: 80 additions & 31 deletions apps/nextjs-app/src/features/app/blocks/view/search/SearchCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Toggle,
} from '@teable/ui-lib';
import { useTranslation } from 'next-i18next';
import { useCallback, useMemo } from 'react';
import { useCallback, useMemo, useState } from 'react';

interface ISearchCommand {
value: string;
Expand Down Expand Up @@ -56,43 +56,33 @@ export const SearchCommand = (props: ISearchCommand) => {

const enableGlobalSearch = value === 'all_fields';

const [filterText, setFilterText] = useState('');

return (
<Command filter={commandFilter}>
<div className="flex items-center justify-around gap-1">
<Toggle
pressed={enableGlobalSearch}
onPressedChange={(pressed) => {
onChange(pressed ? ['all_fields'] : null);
}}
size={'sm'}
className="flex flex-1 items-center p-0"
>
<span className="text-sm">{t('actions.globalSearch')}</span>
</Toggle>

<Toggle
pressed={!!hideNotMatchRow}
onPressedChange={(pressed) => {
onHideSwitchChange(pressed);
}}
size={'sm'}
className="flex flex-1 items-center truncate p-0"
>
<span className="truncate text-sm" title={t('actions.hideNotMatchRow')}>
{t('actions.hideNotMatchRow')}
</span>
</Toggle>
</div>
{!enableGlobalSearch && (
{
<>
<CommandInput placeholder={t('actions.search')} className="h-8 text-xs" />
<CommandInput
placeholder={t('actions.search')}
className="h-8 text-xs"
disabled={enableGlobalSearch}
value={filterText}
onValueChange={(value) => {
setFilterText(value);
}}
/>
<CommandList className="my-2 max-h-64">
{<CommandEmpty>{t('listEmptyTips')}</CommandEmpty>}
{fields.map((field) => {
const { id, name, type, isLookup } = field;
const { Icon } = fieldStaticGetter(type, isLookup);
return (
<CommandItem className="flex flex-1 truncate p-0" key={id} value={id}>
<CommandItem
className="flex flex-1 truncate p-0"
key={id}
value={id}
disabled={enableGlobalSearch}
>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
Expand All @@ -104,7 +94,7 @@ export const SearchCommand = (props: ISearchCommand) => {
<Switch
id={id}
className="scale-75"
checked={selectedFields.includes(id)}
checked={selectedFields.includes(id) || enableGlobalSearch}
onCheckedChange={(checked) => {
switchChange(id, checked);
}}
Expand Down Expand Up @@ -132,7 +122,66 @@ export const SearchCommand = (props: ISearchCommand) => {
})}
</CommandList>
</>
)}
}

<div className="flex flex-col gap-y-1">
<div className="flex items-center justify-around gap-1">
<Toggle
pressed={enableGlobalSearch}
onPressedChange={() => {
onChange(['all_fields']);
setFilterText('');
}}
size={'sm'}
className="flex flex-1 items-center truncate p-0"
>
<span className="truncate text-sm" title={t('actions.hideNotMatchRow')}>
{t('actions.globalSearch')}
</span>
</Toggle>

<Toggle
pressed={!enableGlobalSearch}
onPressedChange={() => {
onChange(null);
}}
size={'sm'}
className="flex flex-1 items-center truncate p-0"
>
<span className="truncate text-sm" title={t('actions.hideNotMatchRow')}>
{t('actions.fieldSearch')}
</span>
</Toggle>
</div>

<div className="flex items-center justify-around gap-1">
<Toggle
pressed={!hideNotMatchRow}
onPressedChange={() => {
onHideSwitchChange(false);
}}
size={'sm'}
className="flex flex-1 items-center truncate p-0"
>
<span className="truncate text-sm" title={t('actions.hideNotMatchRow')}>
{t('actions.showAllRow')}
</span>
</Toggle>

<Toggle
pressed={!!hideNotMatchRow}
onPressedChange={() => {
onHideSwitchChange(true);
}}
size={'sm'}
className="flex flex-1 items-center truncate p-0"
>
<span className="truncate text-sm" title={t('actions.hideNotMatchRow')}>
{t('actions.hideNotMatchRow')}
</span>
</Toggle>
</div>
</div>
</Command>
);
};
1 change: 1 addition & 0 deletions packages/common-i18n/src/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"permanentDelete": "Permanent Delete",
"globalSearch": "Global Search",
"fieldSearch": "Search Field",
"showAllRow": "Display all rows",
"hideNotMatchRow": "Hide not match row"
},
"quickAction": {
Expand Down
1 change: 1 addition & 0 deletions packages/common-i18n/src/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"permanentDelete": "永久删除",
"globalSearch": "全局搜索",
"fieldSearch": "字段搜索",
"showAllRow": "显示全部",
"hideNotMatchRow": "仅显示匹配行"
},
"quickAction": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ const darkTheme = {
interactionLineColorHighlight: colors.violet[700],

// search cursor
searchCursorBg: hexToRGBA(colors.orange[400]),
searchTargetIndexBg: hexToRGBA(colors.yellow[400]),
searchCursorBg: colors.orange[400],
searchTargetIndexBg: colors.yellow[700],

// comment
commentCountBg: colors.orange[400],
Expand Down
4 changes: 2 additions & 2 deletions packages/sdk/src/components/grid/configs/gridTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ export const gridTheme: IGridTheme = {
interactionLineColorHighlight: colors.violet[500],

// search cursor
searchCursorBg: hexToRGBA(colors.amber[300]),
searchTargetIndexBg: hexToRGBA(colors.yellow[300]),
searchCursorBg: colors.amber[400],
searchTargetIndexBg: colors.yellow[200],

// comment
commentCountBg: colors.orange[400],
Expand Down
4 changes: 3 additions & 1 deletion packages/sdk/src/context/aggregation/RowCountProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ export const RowCountProvider: FC<RowCountProviderProps> = ({ children }) => {
prevQueryRef.current &&
!hasChangesExceptWithKey(prevQueryRef.current, rowCountQuery, 'search') &&
searchQuery !== undefined &&
!searchQuery?.[2]
!searchQuery?.[2] &&
prevQueryRef.current.search?.[2] === searchQuery?.[2]
) {
// do not update row count when search is display all rows without other view condition changes
return ReactQueryKeys.rowCount(shareId || (tableId as string), prevQueryRef.current);
}
prevQueryRef.current = rowCountQuery;
Expand Down

0 comments on commit d4b0b96

Please sign in to comment.