diff --git a/models/core/src/core.ts b/models/core/src/core.ts index dc758576c9..d17903413e 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -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> - attachedTo?: Ref - attachedToClass?: Ref> + @Prop(TypeRef(core.class.Doc), core.string.AttachedTo) + @Index(IndexKind.Indexed) + @Hidden() + attachedTo?: Ref + + @Prop(TypeRef(core.class.Doc), core.string.AttachedToClass) + @Index(IndexKind.Indexed) + @Hidden() + attachedToClass?: Ref> // Indexable attributes of document. attributes!: Record removed!: boolean - // States for diffetent stages + // States for different stages stages!: Record } @@ -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 extends TClass implements IndexingConfiguration { + indexes!: FieldIndex[] +} diff --git a/models/core/src/index.ts b/models/core/src/index.ts index 4289458204..dcf4b80639 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -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>, IndexingConfiguration>>( + core.class.TxCollectionCUD, + core.class.Class, + core.mixin.IndexConfiguration, + { + indexes: [ + 'tx.objectId', + 'tx._class', + 'tx.objectClass', + 'tx.operations.attachedTo', + 'objectSpace', + { + objectSpace: 1, + _id: 1 + } + ] + } + ) } diff --git a/models/core/src/tx.ts b/models/core/src/tx.ts index 1d7f514c74..37333bb2a0 100644 --- a/models/core/src/tx.ts +++ b/models/core/src/tx.ts @@ -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 + @Prop(TypeRef(core.class.Space), core.string.Space) + @Index(IndexKind.Indexed) + @Hidden() + objectSpace!: Ref } @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 extends TTx implements TxCUD { + @Prop(TypeRef(core.class.Doc), core.string.Object) @Index(IndexKind.Indexed) + @Hidden() objectId!: Ref - objectClass!: Ref> + @Prop(TypeRef(core.class.Class), core.string.ClassLabel) + @Index(IndexKind.Indexed) + @Hidden() + objectClass!: Ref> } @Model(core.class.TxCreateDoc, core.class.TxCUD) diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 63e3ceb2dd..5e8f6d4bcf 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -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 @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 | null @Prop(TypeRef(tracker.class.Project), tracker.string.Project) + @Index(IndexKind.Indexed) project!: Ref | 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 | 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 } diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 16ff420ec6..7736e4ada5 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -391,7 +391,7 @@ export interface FullTextSearchContext extends Class { 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>[] } @@ -401,7 +401,7 @@ export interface FullTextSearchContext extends Class { export interface ConfigurationElement extends Class { // 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 { * * 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 + +/** + * @public + */ +export enum IndexOrder { + Ascending = 1, + Descending = -1 +} + +/** + * @public + */ +export type FieldIndex = { + [P in keyof T]?: IndexOrder +} & { + [key: string]: IndexOrder +} + +/** + * @public + * + * Mixin for extra indexing fields. + */ +export interface IndexingConfiguration extends Class { + // Define a list of extra index definitions. + indexes: (FieldIndex | string)[] +} diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 1f65235548..820ea629d7 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -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[] = [] diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 1cb214335d..3b9d319ab6 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -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>, - ConfigurationElement: '' as Ref> + ConfigurationElement: '' as Ref>, + IndexConfiguration: '' as Ref>> }, space: { Tx: '' as Ref, diff --git a/plugins/tracker-assets/lang/en.json b/plugins/tracker-assets/lang/en.json index 3d4d1a159d..a312547ec8 100644 --- a/plugins/tracker-assets/lang/en.json +++ b/plugins/tracker-assets/lang/en.json @@ -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", diff --git a/server/tool/src/index.ts b/server/tool/src/index.ts index 8581fe8152..ed1b957a12 100644 --- a/server/tool/src/index.ts +++ b/server/tool/src/index.ts @@ -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>() + const domains = new Map>>() // 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() + const domainAttrs = domains.get(domain) ?? new Set>() 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)[] = [] for (const vv of v.values()) { await collection.createIndex(vv) bb.push(vv) diff --git a/tests/sanity/tests/tracker.projects.spec.ts b/tests/sanity/tests/tracker.projects.spec.ts index 4aac0f43d0..45801d4583 100644 --- a/tests/sanity/tests/tracker.projects.spec.ts +++ b/tests/sanity/tests/tracker.projects.spec.ts @@ -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")') diff --git a/tests/sanity/tests/tracker.utils.ts b/tests/sanity/tests/tracker.utils.ts index fe7e5abd0c..64701ba866 100644 --- a/tests/sanity/tests/tracker.utils.ts +++ b/tests/sanity/tests/tracker.utils.ts @@ -84,7 +84,7 @@ export async function createIssue (page: Page, props: IssueProps): Promise 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' }) }