Saved filter improve (#3180)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-05-16 01:29:31 +06:00 committed by GitHub
parent d137e5548c
commit 696541af2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 319 additions and 99 deletions

View File

@ -13,23 +13,22 @@
// limitations under the License.
//
import type { Class, Client, Data, Doc, DocumentQuery, Ref, Space } from '@hcengineering/core'
import type { Account, Class, Client, Data, Doc, DocumentQuery, Domain, Ref, Space } from '@hcengineering/core'
import { DOMAIN_MODEL } from '@hcengineering/core'
import { Builder, Mixin, Model } from '@hcengineering/model'
import core, { TClass, TDoc } from '@hcengineering/model-core'
import preference, { TPreference } from '@hcengineering/model-preference'
import type { Asset, IntlString, Resource, Status } from '@hcengineering/platform'
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
import type { AnyComponent, Location } from '@hcengineering/ui'
import type {
Action,
ActionCategory,
ActivityAttributePresenter,
AllValuesFunc,
ArrayEditor,
AttributeEditor,
AttributeFilter,
AttributePresenter,
ActivityAttributePresenter,
BuildModelKey,
ClassFilters,
ClassSortFuncs,
@ -57,9 +56,9 @@ import type {
ObjectValidator,
PreviewPresenter,
SortFunc,
SpacePresenter,
SpaceHeader,
SpaceName,
SpacePresenter,
ViewAction,
ViewActionInput,
ViewContext,
@ -75,6 +74,8 @@ export { viewId } from '@hcengineering/view'
export { viewOperation } from './migration'
export { ViewAction, Viewlet }
export const DOMAIN_VIEW = 'view' as Domain
export function createAction<T extends Doc = Doc, P = Record<string, any>> (
builder: Builder,
data: Data<Action<T, P>>,
@ -108,14 +109,18 @@ export function classPresenter (
}
}
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_PREFERENCE)
export class TFilteredView extends TPreference implements FilteredView {
@Model(view.class.FilteredView, core.class.Doc, DOMAIN_VIEW)
export class TFilteredView extends TDoc implements FilteredView {
name!: string
location!: Location
filters!: string
viewOptions?: ViewOptions
filterClass?: Ref<Class<Doc>>
viewletId?: Ref<Viewlet> | null
users!: Ref<Account>[]
createdBy!: Ref<Account>
attachedTo!: string
sharable?: boolean
}
@Model(view.class.FilterMode, core.class.Doc, DOMAIN_MODEL)

View File

@ -13,20 +13,11 @@
// limitations under the License.
//
import core, {
AnyAttribute,
Class,
Doc,
DOMAIN_TX,
Ref,
TxCreateDoc,
TxCUD,
TxProcessor,
TxRemoveDoc
} from '@hcengineering/core'
import core, { AnyAttribute, DOMAIN_TX, Ref, TxCreateDoc, TxCUD, TxProcessor, TxRemoveDoc } from '@hcengineering/core'
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
import { DOMAIN_PREFERENCE } from '@hcengineering/preference'
import { BuildModelKey, FilteredView, Viewlet, ViewletPreference } from '@hcengineering/view'
import { DOMAIN_VIEW } from '.'
import view from './plugin'
async function migrateViewletPreference (client: MigrationClient): Promise<void> {
@ -68,41 +59,25 @@ async function migrateViewletPreference (client: MigrationClient): Promise<void>
}
async function migrateSavedFilters (client: MigrationClient): Promise<void> {
const preferences = await client.find<FilteredView>(DOMAIN_PREFERENCE, {
await client.move(
DOMAIN_PREFERENCE,
{
_class: view.class.FilteredView
},
DOMAIN_VIEW
)
const preferences = await client.find<FilteredView>(DOMAIN_VIEW, {
_class: view.class.FilteredView,
viewOptions: { $exists: true }
users: { $exists: false }
})
for (const pref of preferences) {
if (pref.viewOptions === undefined) continue
if (Array.isArray(pref.viewOptions.groupBy)) continue
pref.viewOptions.groupBy = [pref.viewOptions.groupBy]
await client.update<FilteredView>(
DOMAIN_PREFERENCE,
DOMAIN_VIEW,
{
_id: pref._id
},
{
viewOptions: pref.viewOptions
}
)
}
}
async function migrateSavedFiltersViewlets (client: MigrationClient): Promise<void> {
const preferences = await client.find<FilteredView>(DOMAIN_PREFERENCE, {
_class: view.class.FilteredView,
viewletId: /^\S{24}$/ as any,
attachedTo: 'tracker' as any
})
for (const pref of preferences) {
await client.update<FilteredView>(
DOMAIN_PREFERENCE,
{
_id: pref._id
},
{
viewletId: 'tracker:viewlet:IssueList' as Ref<Viewlet>,
filterClass: 'tracker:class:Issue' as Ref<Class<Doc>>
users: [pref.createdBy]
}
)
}
@ -178,7 +153,6 @@ export const viewOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {
await migrateViewletPreference(client)
await migrateSavedFilters(client)
await migrateSavedFiltersViewlets(client)
await fixViewletPreferenceRemovedAttributes(client)
await fixPreferenceObjectKey(client)
},

View File

@ -78,6 +78,10 @@
"ThisMonth": "This month",
"NextMonth": "Next month",
"NotSpecified": "Not specified",
"CustomDate": "Custom date"
"CustomDate": "Custom date",
"AddSavedView": "Add saved view",
"Public": "Public",
"Hide": "Hide",
"SaveAs": "Save as"
}
}

View File

@ -75,6 +75,10 @@
"ThisMonth": "Этот месяц",
"NextMonth": "Следующий месяц",
"NotSpecified": "Не указана",
"CustomDate": "Выбранная дата"
"AddSavedView": "Добавить сохраненное отображение",
"CustomDate": "Выбранная дата",
"Public": "Публичныйы",
"Hide": "Спрятать",
"SaveAs": "Сохранить как"
}
}

View File

@ -0,0 +1,75 @@
<script lang="ts">
import { getCurrentAccount } from '@hcengineering/core'
import { translate } from '@hcengineering/platform'
import presentation, { createQuery, getClient } from '@hcengineering/presentation'
import { deviceOptionsStore, resizeObserver } from '@hcengineering/ui'
import { FilteredView } from '@hcengineering/view'
import { createEventDispatcher, onMount } from 'svelte'
import view from '../../plugin'
export let attachedTo: string | undefined
const me = getCurrentAccount()._id
const q = createQuery()
let views: FilteredView[] = []
const baseQuery = {
attachedTo,
sharable: true,
createdBy: { $ne: me }
}
$: query =
search === ''
? baseQuery
: {
...baseQuery,
name: { $like: `%${search}%` }
}
$: q.query(view.class.FilteredView, query, (res) => {
views = res.filter((p) => !p.users.includes(me))
})
const client = getClient()
const dispatch = createEventDispatcher()
async function add (sv: FilteredView): Promise<void> {
await client.update(sv, { $push: { users: me } })
dispatch('close')
}
let search: string = ''
let phTraslate: string = ''
let searchInput: HTMLInputElement
$: translate(presentation.string.Search, {}).then((res) => {
phTraslate = res
})
onMount(() => {
if (searchInput && !$deviceOptionsStore.isMobile) searchInput.focus()
})
</script>
<div class="selectPopup" use:resizeObserver={() => dispatch('changeContent')}>
<div class="header">
<input bind:this={searchInput} type="text" bind:value={search} placeholder={phTraslate} />
</div>
<div class="scroll">
<div class="box">
{#each views as value}
<button
class="menu-item no-focus"
on:click={() => {
add(value)
}}
>
<div class="flex-row-center w-full">
{value.name}
</div>
</button>
{/each}
</div>
</div>
</div>

View File

@ -13,17 +13,19 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
import { Class, Doc, DocumentQuery, Ref, getCurrentAccount } from '@hcengineering/core'
import { getResource } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Button, IconAdd, eventToHTMLElement, showPopup } from '@hcengineering/ui'
import { Filter, ViewOptions } from '@hcengineering/view'
import { Button, IconAdd, eventToHTMLElement, getCurrentLocation, showPopup } from '@hcengineering/ui'
import { Filter, FilteredView, ViewOptions, Viewlet } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { filterStore, removeFilter, updateFilter } from '../../filter'
import { filterStore, removeFilter, updateFilter, selectedFilterStore } from '../../filter'
import view from '../../plugin'
import FilterSave from './FilterSave.svelte'
import FilterSection from './FilterSection.svelte'
import FilterTypePopup from './FilterTypePopup.svelte'
import { activeViewlet, getActiveViewletId, makeViewletKey } from '../../utils'
import { getViewOptions, viewOptionStore } from '../../viewOptions'
export let _class: Ref<Class<Doc>>
export let query: DocumentQuery<Doc>
@ -58,6 +60,23 @@
showPopup(FilterSave, { viewOptions, _class })
}
async function saveCurrentFilteredView (filter: FilteredView | undefined) {
if (filter !== undefined) {
const filters = JSON.stringify($filterStore)
await client.update(filter, {
filters,
viewOptions,
viewletId: getActiveViewletId()
})
selectedFilterStore.set({
...filter,
filters,
viewOptions,
viewletId: getActiveViewletId()
})
}
}
async function makeQuery (query: DocumentQuery<Doc>, filters: Filter[]): Promise<void> {
const newQuery = hierarchy.clone(query)
for (let i = 0; i < filters.length; i++) {
@ -125,6 +144,27 @@
$: clazz = hierarchy.getClass(_class)
$: visible = hierarchy.hasMixin(clazz, view.mixin.ClassFilters)
const me = getCurrentAccount()._id
function selectedFilterChanged (
selectedFilter: FilteredView | undefined,
filters: Filter[],
activeViewlet: Record<string, Ref<Viewlet> | null>,
viewOptionStore: Map<string, ViewOptions>
): boolean {
if (selectedFilter === undefined) return false
if (selectedFilter.createdBy !== me) return false
const loc = getCurrentLocation()
const key = makeViewletKey(loc)
if (selectedFilter.viewletId !== activeViewlet[key]) return true
if (selectedFilter.filters !== JSON.stringify(filters)) return true
if (selectedFilter.viewletId !== null) {
const viewOptions = getViewOptions({ _id: selectedFilter.viewletId } as Viewlet, viewOptionStore)
if (JSON.stringify(selectedFilter.viewOptions) !== JSON.stringify(viewOptions)) return true
}
return false
}
</script>
{#if visible && $filterStore && $filterStore.length > 0}
@ -135,6 +175,7 @@
{filter}
on:change={() => {
makeQuery(query, $filterStore)
updateFilter(filter)
}}
on:remove={() => {
removeFilter(i)
@ -146,7 +187,22 @@
</div>
</div>
<Button icon={view.icon.Views} label={view.string.Save} width={'fit-content'} on:click={() => saveFilteredView()} />
<div class="flex gap-1-5">
<Button
icon={view.icon.Views}
label={view.string.SaveAs}
width={'fit-content'}
on:click={() => saveFilteredView()}
/>
{#if selectedFilterChanged($selectedFilterStore, $filterStore, $activeViewlet, $viewOptionStore)}
<Button
icon={view.icon.Views}
label={view.string.Save}
width={'fit-content'}
on:click={() => saveCurrentFilteredView($selectedFilterStore)}
/>
{/if}
</div>
</div>
{/if}

View File

@ -15,9 +15,9 @@
<script lang="ts">
import { Class, Doc, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Button, IconClose, eventToHTMLElement, showPopup } from '@hcengineering/ui'
import { Button, IconClose, eventToHTMLElement, resolvedLocationStore, showPopup } from '@hcengineering/ui'
import { Filter } from '@hcengineering/view'
import { filterStore, setFilters } from '../../filter'
import { filterStore, getFilterKey, setFilters } from '../../filter'
import view from '../../plugin'
import FilterTypePopup from './FilterTypePopup.svelte'
import IconFilter from '../icons/Filter.svelte'
@ -29,6 +29,25 @@
const client = getClient()
const hierarchy = client.getHierarchy()
resolvedLocationStore.subscribe(() => {
load(_class)
})
function load (_class: Ref<Class<Doc>> | undefined) {
const key = getFilterKey(_class)
const items = localStorage.getItem(key)
if (items !== null) {
filterStore.set(JSON.parse(items))
}
}
function save (_class: Ref<Class<Doc>> | undefined, p: Filter[]) {
const key = getFilterKey(_class)
localStorage.setItem(key, JSON.stringify(p))
}
filterStore.subscribe((p) => save(_class, p))
function onChange (e: Filter | undefined) {
if (e !== undefined) setFilters([e])
}

View File

@ -1,8 +1,8 @@
<script lang="ts">
import { Class, Doc, Ref } from '@hcengineering/core'
import { Class, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
import preference from '@hcengineering/preference'
import { Card, getClient } from '@hcengineering/presentation'
import { Button, EditBox, getCurrentResolvedLocation } from '@hcengineering/ui'
import { Button, EditBox, ToggleWithLabel, getCurrentResolvedLocation } from '@hcengineering/ui'
import { ViewOptions } from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import { filterStore } from '../../filter'
@ -12,6 +12,9 @@
export let viewOptions: ViewOptions | undefined = undefined
export let _class: Ref<Class<Doc>>
const me = getCurrentAccount()._id
let sharable = true
let filterName = ''
const client = getClient()
@ -27,7 +30,9 @@
filters,
attachedTo: loc.path[2] as Ref<Doc>,
viewOptions,
viewletId: getActiveViewletId()
viewletId: getActiveViewletId(),
sharable,
users: [me]
})
}
@ -57,4 +62,5 @@
/>
</div>
</div>
<ToggleWithLabel bind:on={sharable} label={view.string.Public} />
</Card>

View File

@ -13,7 +13,7 @@ import core, {
import { getResource } from '@hcengineering/platform'
import { LiveQuery, createQuery, getClient } from '@hcengineering/presentation'
import { AnyComponent, locationToUrl, getCurrentResolvedLocation } from '@hcengineering/ui'
import { Filter, FilterMode, KeyFilter } from '@hcengineering/view'
import { Filter, FilterMode, FilteredView, KeyFilter } from '@hcengineering/view'
import { get, writable } from 'svelte/store'
import view from './plugin'
@ -22,6 +22,11 @@ import view from './plugin'
*/
export const filterStore = writable<Filter[]>([])
/**
* @public
*/
export const selectedFilterStore = writable<FilteredView | undefined>()
export function setFilters (filters: Filter[]): void {
const old = get(filterStore)
old.forEach((p) => p.onRemove?.())
@ -288,9 +293,14 @@ export function createFilter (_class: Ref<Class<Doc>>, key: string, value: any[]
}
}
export function getFilterKey (_class: Ref<Class<Doc>>): string {
export function getFilterKey (_class: Ref<Class<Doc>> | undefined): string {
const loc = getCurrentResolvedLocation()
loc.path.length = 3
loc.fragment = undefined
loc.query = undefined
return 'filter' + locationToUrl(loc) + _class
let res = 'filter' + locationToUrl(loc)
if (_class !== undefined) {
res = res + _class
}
return res
}

View File

@ -102,6 +102,7 @@ export { getActions, invokeAction } from './actions'
export { default as ActionContext } from './components/ActionContext.svelte'
export { default as ActionHandler } from './components/ActionHandler.svelte'
export { default as FilterButton } from './components/filter/FilterButton.svelte'
export { default as AddSavedView } from './components/filter/AddSavedView.svelte'
export { default as FixedColumn } from './components/FixedColumn.svelte'
export { default as SourcePresenter } from './components/inference/SourcePresenter.svelte'
export { default as LinkPresenter } from './components/LinkPresenter.svelte'

View File

@ -76,7 +76,8 @@ export default mergeIds(viewId, view, {
ThisMonth: '' as IntlString,
NextMonth: '' as IntlString,
NotSpecified: '' as IntlString,
CustomDate: '' as IntlString
CustomDate: '' as IntlString,
SaveAs: '' as IntlString
},
function: {
StatusSort: '' as SortFunc

View File

@ -15,6 +15,7 @@
//
import type {
Account,
AnyAttribute,
CategoryType,
Class,
@ -35,7 +36,7 @@ import type {
Type,
UXObject
} from '@hcengineering/core'
import { Asset, IntlString, Plugin, plugin, Resource, Status } from '@hcengineering/platform'
import { Asset, IntlString, Plugin, Resource, Status, plugin } from '@hcengineering/platform'
import type { Preference } from '@hcengineering/preference'
import type {
AnyComponent,
@ -99,13 +100,17 @@ export interface Filter {
/**
* @public
*/
export interface FilteredView extends Preference {
export interface FilteredView extends Doc {
name: string
location: PlatformLocation
filters: string
viewOptions?: ViewOptions
filterClass?: Ref<Class<Doc>>
viewletId?: Ref<Viewlet> | null
sharable?: boolean
users: Ref<Account>[]
createdBy: Ref<Account>
attachedTo: string
}
/**
@ -707,7 +712,10 @@ const view = plugin(viewId, {
SelectToMove: '' as IntlString,
Cancel: '' as IntlString,
List: '' as IntlString,
Timeline: '' as IntlString
AddSavedView: '' as IntlString,
Timeline: '' as IntlString,
Public: '' as IntlString,
Hide: '' as IntlString
},
icon: {
Table: '' as Asset,

View File

@ -1,16 +1,18 @@
<script lang="ts">
import { Doc, Ref } from '@hcengineering/core'
import { Ref, getCurrentAccount } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
import { Action, Location, location, navigate } from '@hcengineering/ui'
import { Action, IconAdd, Location, eventToHTMLElement, location, navigate, showPopup } from '@hcengineering/ui'
import view, { Filter, FilteredView, ViewOptions, Viewlet } from '@hcengineering/view'
import {
AddSavedView,
TreeItem,
TreeNode,
activeViewlet,
filterStore,
getViewOptions,
makeViewletKey,
selectedFilterStore,
setActiveViewletId,
setFilters,
setViewOptions,
@ -23,16 +25,15 @@
const dispatch = createEventDispatcher()
const client = getClient()
const me = getCurrentAccount()._id
const filteredViewsQuery = createQuery()
let filteredViews: FilteredView[] | undefined
$: filteredViewsQuery.query(
view.class.FilteredView,
{ attachedTo: currentApplication?.alias as Ref<Doc> },
(result) => {
filteredViews = result
}
)
let availableFilteredViews: FilteredView[] = []
let myFilteredViews: FilteredView[] = []
$: filteredViewsQuery.query(view.class.FilteredView, { attachedTo: currentApplication?.alias }, (result) => {
myFilteredViews = result.filter((p) => p.users.includes(me))
availableFilteredViews = result.filter((p) => p.sharable && !p.users.includes(me))
})
async function removeAction (filteredView: FilteredView): Promise<Action[]> {
return [
@ -46,9 +47,27 @@
]
}
async function viewAction (filteredView: FilteredView): Promise<Action[]> {
if (filteredView.createdBy === me) return await removeAction(filteredView)
return await hideAction(filteredView)
}
async function hideAction (object: FilteredView): Promise<Action[]> {
return [
{
icon: view.icon.Archive,
label: view.string.Hide,
action: async (ctx: any, evt: Event) => {
await client.update(object, { $pull: { users: me } })
}
}
]
}
let selectedId: Ref<FilteredView> | undefined = undefined
async function load (fv: FilteredView): Promise<void> {
selectedFilterStore.set(fv)
navigate({
path: fv.location.path,
query: fv.location.query ?? undefined,
@ -65,10 +84,28 @@
}
const clearSelection = () => {
selectedFilterStore.set(undefined)
selectedId = undefined
dispatch('select', false)
}
function checkFilter (
fv: FilteredView,
loc: Location,
filters: string,
viewOptionStore: Map<string, ViewOptions>
): boolean {
if (fv.location.path.join() !== loc.path.join()) return false
if (fv.filters !== filters) return false
const key = makeViewletKey(loc)
if (fv.viewletId !== $activeViewlet[key]) return false
if (fv.viewletId !== null) {
const viewOptions = getViewOptions({ _id: fv.viewletId } as Viewlet, viewOptionStore)
if (JSON.stringify(fv.viewOptions) !== JSON.stringify(viewOptions)) return false
}
return true
}
function checkSelected (
fs: Filter[],
loc: Location,
@ -77,18 +114,19 @@
) {
const filters = JSON.stringify(fs)
if (loc && Array.isArray(fs) && fs.length > 0 && Array.isArray(filteredViews)) {
for (const fv of filteredViews) {
if (fv.location.path.join() !== loc.path.join()) continue
if (fv.filters !== filters) continue
const key = makeViewletKey(loc)
if (fv.viewletId !== $activeViewlet[key]) continue
if (fv.viewletId !== null) {
const viewOptions = getViewOptions({ _id: fv.viewletId } as Viewlet, viewOptionStore)
if (JSON.stringify(fv.viewOptions) !== JSON.stringify(viewOptions)) continue
if ($selectedFilterStore !== undefined) {
if ($selectedFilterStore.location.path.join() === loc.path.join()) {
selectedId = $selectedFilterStore._id
dispatch('select', true)
return
}
}
for (const fv of filteredViews) {
if (checkFilter(fv, loc, filters, viewOptionStore)) {
selectedId = fv._id
dispatch('select', true)
return
}
selectedId = fv._id
dispatch('select', true)
return
}
clearSelection()
} else {
@ -96,19 +134,36 @@
}
}
$: checkSelected($filterStore, $location, filteredViews, $viewOptionStore)
$: dispatch('shown', filteredViews !== undefined && filteredViews.length > 0)
$: checkSelected($filterStore, $location, myFilteredViews, $viewOptionStore)
$: shown = myFilteredViews.length > 0 || availableFilteredViews.length > 0
$: dispatch('shown', shown)
async function getActions (availableFilteredViews: FilteredView[]): Promise<Action[]> {
if (availableFilteredViews.length > 0) {
const add: Action = {
label: view.string.AddSavedView,
icon: IconAdd,
action: async (_, e): Promise<void> => {
showPopup(AddSavedView, { attachedTo: currentApplication?.alias }, eventToHTMLElement(e as MouseEvent))
}
}
return [add]
} else {
return []
}
}
</script>
{#if filteredViews && filteredViews.length > 0}
<TreeNode label={view.string.FilteredViews} parent>
{#each filteredViews as fv}
{#if shown}
<TreeNode label={view.string.FilteredViews} parent actions={async () => getActions(availableFilteredViews)}>
{#each myFilteredViews as fv}
<TreeItem
_id={fv._id}
title={fv.name}
selected={selectedId === fv._id}
on:click={() => load(fv)}
actions={() => removeAction(fv)}
actions={() => viewAction(fv)}
/>
{/each}
</TreeNode>

View File

@ -170,7 +170,7 @@ export class FullTextIndex implements WithFind {
let { docs, pass } = await this.indexer.search(classes, findQuery, fullTextLimit)
if (docs.length === 0 && pass) {
docs = [...docs, ...(await this.adapter.search(classes, query, fullTextLimit))]
docs = await this.adapter.search(classes, query, fullTextLimit)
}
const indexedDocMap = new Map<Ref<Doc>, IndexedDoc>()

View File

@ -115,10 +115,6 @@ export class FullTextPushStage implements FullTextPipelineStage {
if (pipeline.cancelling) {
return
}
if (pipeline.cancelling) {
return
}
const elasticDoc = createElasticDoc(doc)
try {
updateDoc2Elastic(doc.attributes, elasticDoc)
@ -169,12 +165,12 @@ export class FullTextPushStage implements FullTextPipelineStage {
// Perform bulk update to elastic
try {
await this.fulltextAdapter.updateMany(bulk)
for (const doc of toIndex) {
await pipeline.update(doc._id, true, {})
}
} catch (err: any) {
console.error(err)
}
for (const doc of toIndex) {
await pipeline.update(doc._id, true, {})
}
}
}

View File

@ -418,6 +418,9 @@ export class FullTextIndexPipeline implements FullTextPipeline {
{ collector: st.stageId },
async (ctx) => await st.collect(toIndex, this, ctx)
)
if (this.cancelling) {
break
}
toIndex.forEach((it) => _classUpdate.add(it.objectClass))
@ -432,6 +435,9 @@ export class FullTextIndexPipeline implements FullTextPipeline {
async (ctx) => await nst.collect(toIndex2, this, ctx)
)
}
if (this.cancelling) {
break
}
}
} else {
break