TSK-803: Fix load speed (#2728)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-03-13 23:50:10 +07:00 committed by GitHub
parent b6980f307e
commit 9eddd84ada
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 111 additions and 21 deletions

View File

@ -34,8 +34,10 @@ import {
DOMAIN_MODEL,
Enum,
EnumOf,
FieldIndex,
FullTextData,
FullTextSearchContext,
IndexingConfiguration,
IndexKind,
IndexStageState,
Interface,
@ -249,15 +251,22 @@ export class TFulltextData extends TDoc implements FullTextData {
export class TDocIndexState extends TDoc implements DocIndexState {
objectClass!: Ref<Class<Doc>>
attachedTo?: Ref<Doc>
attachedToClass?: Ref<Class<Doc>>
@Prop(TypeRef(core.class.Doc), core.string.AttachedTo)
@Index(IndexKind.Indexed)
@Hidden()
attachedTo?: Ref<Doc>
@Prop(TypeRef(core.class.Doc), core.string.AttachedToClass)
@Index(IndexKind.Indexed)
@Hidden()
attachedToClass?: Ref<Class<Doc>>
// Indexable attributes of document.
attributes!: Record<string, any>
removed!: boolean
// States for diffetent stages
// States for different stages
stages!: Record<string, boolean | string>
}
@ -284,3 +293,8 @@ export class TConfiguration extends TDoc implements Configuration {
@Prop(TypeBoolean(), core.string.Private)
enabled!: boolean
}
@MMixin(core.mixin.IndexConfiguration, core.class.Class)
export class TIndexConfiguration<T extends Doc = Doc> extends TClass implements IndexingConfiguration<T> {
indexes!: FieldIndex<T>[]
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import { AccountRole } from '@hcengineering/core'
import { AccountRole, TxCollectionCUD, Doc, AttachedDoc, IndexingConfiguration, Class } from '@hcengineering/core'
import { Builder } from '@hcengineering/model'
import core from './component'
import {
@ -31,6 +31,7 @@ import {
TEnumOf,
TFulltextData,
TFullTextSearchContext,
TIndexConfiguration,
TIndexStageState,
TInterface,
TMixin,
@ -104,7 +105,8 @@ export function createModel (builder: Builder): void {
TIndexStageState,
TFullTextSearchContext,
TConfiguration,
TConfigurationElement
TConfigurationElement,
TIndexConfiguration
)
builder.createDoc(
@ -116,4 +118,23 @@ export function createModel (builder: Builder): void {
},
core.account.System
)
builder.mixin<Class<TxCollectionCUD<Doc, AttachedDoc>>, IndexingConfiguration<TxCollectionCUD<Doc, AttachedDoc>>>(
core.class.TxCollectionCUD,
core.class.Class,
core.mixin.IndexConfiguration,
{
indexes: [
'tx.objectId',
'tx._class',
'tx.objectClass',
'tx.operations.attachedTo',
'objectSpace',
{
objectSpace: 1,
_id: 1
}
]
}
)
}

View File

@ -35,7 +35,7 @@ import {
TxRemoveDoc,
TxUpdateDoc
} from '@hcengineering/core'
import { Index, Model } from '@hcengineering/model'
import { Hidden, Index, Model, Prop, TypeRef } from '@hcengineering/model'
import core from './component'
import { TDoc } from './core'
@ -43,7 +43,10 @@ import { TDoc } from './core'
@Model(core.class.Tx, core.class.Doc, DOMAIN_TX)
export class TTx extends TDoc implements Tx {
objectSpace!: Ref<Space>
@Prop(TypeRef(core.class.Space), core.string.Space)
@Index(IndexKind.Indexed)
@Hidden()
objectSpace!: Ref<Space>
}
@Model(core.class.TxModelUpgrade, core.class.Tx, DOMAIN_TX)
@ -51,10 +54,15 @@ export class TTxModelUpgrade extends TTx {}
@Model(core.class.TxCUD, core.class.Tx)
export class TTxCUD<T extends Doc> extends TTx implements TxCUD<T> {
@Prop(TypeRef(core.class.Doc), core.string.Object)
@Index(IndexKind.Indexed)
@Hidden()
objectId!: Ref<T>
objectClass!: Ref<Class<T>>
@Prop(TypeRef(core.class.Class), core.string.ClassLabel)
@Index(IndexKind.Indexed)
@Hidden()
objectClass!: Ref<Class<T>>
}
@Model(core.class.TxCreateDoc, core.class.TxCUD)

View File

@ -211,9 +211,11 @@ export class TIssue extends TAttachedDoc implements Issue {
description!: Markup
@Prop(TypeRef(tracker.class.IssueStatus), tracker.string.Status)
@Index(IndexKind.Indexed)
status!: Ref<IssueStatus>
@Prop(TypeIssuePriority(), tracker.string.Priority)
@Index(IndexKind.Indexed)
priority!: IssuePriority
@Prop(TypeNumber(), tracker.string.Number)
@ -221,9 +223,11 @@ export class TIssue extends TAttachedDoc implements Issue {
number!: number
@Prop(TypeRef(contact.class.Employee), tracker.string.Assignee)
@Index(IndexKind.Indexed)
assignee!: Ref<Employee> | null
@Prop(TypeRef(tracker.class.Project), tracker.string.Project)
@Index(IndexKind.Indexed)
project!: Ref<Project> | null
@Prop(Collection(tracker.class.Issue), tracker.string.SubIssues)
@ -256,6 +260,7 @@ export class TIssue extends TAttachedDoc implements Issue {
rank!: string
@Prop(TypeRef(tracker.class.Sprint), tracker.string.Sprint)
@Index(IndexKind.Indexed)
sprint!: Ref<Sprint> | null
@Prop(TypeNumber(), tracker.string.Estimation)
@ -397,6 +402,7 @@ export class TSprint extends TDoc implements Sprint {
description?: Markup
@Prop(TypeSprintStatus(), tracker.string.Status)
@Index(IndexKind.Indexed)
status!: SprintStatus
@Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead)
@ -423,6 +429,7 @@ export class TSprint extends TDoc implements Sprint {
capacity!: number
@Prop(TypeRef(tracker.class.Project), tracker.string.Project)
@Index(IndexKind.Indexed)
project!: Ref<Project>
}

View File

@ -391,7 +391,7 @@ export interface FullTextSearchContext extends Class<Doc> {
fullTextSummary?: boolean
forceIndex?: boolean
// If defined, will propogate changes to childs with defined set of classes
// If defined, will propagate changes to child's with defined set of classes
propogate?: Ref<Class<Doc>>[]
}
@ -401,7 +401,7 @@ export interface FullTextSearchContext extends Class<Doc> {
export interface ConfigurationElement extends Class<Doc> {
// Title will be presented to owner.
title: IntlString
// Group for groupping.
// Group for grouping.
group: IntlString
}
@ -410,7 +410,7 @@ export interface ConfigurationElement extends Class<Doc> {
*
* Define configuration value configuration for workspace.
*
* Configuration is accessble only for owners of workspace and underhood services.
* Configuration is accessible only for owners of workspace and under hood services.
*/
export interface Configuration extends Doc {
enabled: boolean
@ -420,3 +420,30 @@ export interface Configuration extends Doc {
* @public
*/
export type RelatedDocument = Pick<Doc, '_id' | '_class'>
/**
* @public
*/
export enum IndexOrder {
Ascending = 1,
Descending = -1
}
/**
* @public
*/
export type FieldIndex<T extends Doc> = {
[P in keyof T]?: IndexOrder
} & {
[key: string]: IndexOrder
}
/**
* @public
*
* Mixin for extra indexing fields.
*/
export interface IndexingConfiguration<T extends Doc> extends Class<Doc> {
// Define a list of extra index definitions.
indexes: (FieldIndex<T> | string)[]
}

View File

@ -166,12 +166,13 @@ export async function createClient (
}
const conn = await connect(txHandler)
const t = Date.now()
const atxes = await conn.findAll(
core.class.Tx,
{ objectSpace: core.space.Model },
{ sort: { _id: SortingOrder.Ascending } }
)
console.log('find model', atxes.length)
console.log('find model', atxes.length, Date.now() - t)
let systemTx: Tx[] = []
const userTx: Tx[] = []

View File

@ -42,7 +42,8 @@ import type {
UserStatus,
Configuration,
ConfigurationElement,
IndexStageState
IndexStageState,
IndexingConfiguration
} from './classes'
import type {
Tx,
@ -109,7 +110,8 @@ export default plugin(coreId, {
},
mixin: {
FullTextSearchContext: '' as Ref<Mixin<FullTextSearchContext>>,
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
IndexConfiguration: '' as Ref<Mixin<IndexingConfiguration<Doc>>>
},
space: {
Tx: '' as Ref<Space>,

View File

@ -47,7 +47,7 @@
"AddIssue": "Add Issue",
"NewIssue": "New issue",
"ResumeDraft": "Resume draft",
"SaveIssue": "Save issue",
"SaveIssue": "Create issue",
"SetPriority": "Set priority\u2026",
"SetStatus": "Set status\u2026",
"SelectIssue": "Select issue",

View File

@ -17,9 +17,11 @@ import contact from '@hcengineering/contact'
import core, {
BackupClient,
Client as CoreClient,
Doc,
Domain,
DOMAIN_MODEL,
DOMAIN_TX,
FieldIndex,
IndexKind,
Tx,
WorkspaceId
@ -198,7 +200,7 @@ async function createUpdateIndexes (connection: CoreClient, db: Db): Promise<voi
const classes = await connection.findAll(core.class.Class, {})
const hierarchy = connection.getHierarchy()
const domains = new Map<Domain, Set<string>>()
const domains = new Map<Domain, Set<string | FieldIndex<Doc>>>()
// Find all domains and indexed fields inside
for (const c of classes) {
try {
@ -207,22 +209,30 @@ async function createUpdateIndexes (connection: CoreClient, db: Db): Promise<voi
continue
}
const attrs = hierarchy.getAllAttributes(c._id)
const domainAttrs = domains.get(domain) ?? new Set<string>()
const domainAttrs = domains.get(domain) ?? new Set<string | FieldIndex<Doc>>()
for (const a of attrs.values()) {
if (a.index !== undefined && a.index === IndexKind.Indexed) {
domainAttrs.add(a.name)
}
}
// Handle extra configurations
if (hierarchy.hasMixin(c, core.mixin.IndexConfiguration)) {
const config = hierarchy.as(c, core.mixin.IndexConfiguration)
for (const attr of config.indexes) {
domainAttrs.add(attr)
}
}
domains.set(domain, domainAttrs)
} catch (err: any) {
// Ignore, since we have clases without domain.
// Ignore, since we have classes without domain.
}
}
for (const [d, v] of domains.entries()) {
const collection = db.collection(d)
const bb: string[] = []
const bb: (string | FieldIndex<Doc>)[] = []
for (const vv of v.values()) {
await collection.createIndex(vv)
bb.push(vv)

View File

@ -30,7 +30,7 @@ test.describe('project tests', () => {
await page.fill('[placeholder="Issue\\ title"]', 'issue')
await page.click('form button:has-text("Project")')
await page.click(`.selectPopup button:has-text("${prjId}")`)
await page.click('form button:has-text("Save issue")')
await page.click('form button:has-text("Create issue")')
await page.waitForSelector('form.antiCard', { state: 'detached' })
await page.click('.listGrid :has-text("issue")')

View File

@ -84,7 +84,7 @@ export async function createIssue (page: Page, props: IssueProps): Promise<void>
await page.waitForSelector('span:has-text("Default")')
await page.click('button:has-text("New issue")')
await fillIssueForm(page, props)
await page.click('button:has-text("Save issue")')
await page.click('button:has-text("Create issue")')
await page.waitForSelector('form.antiCard', { state: 'detached' })
}