UBER-279: Total qfix (#3281)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-05-30 14:17:03 +07:00 committed by GitHub
parent c0cb5a87e7
commit adda89d0c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 126 additions and 72 deletions

View File

@ -416,6 +416,7 @@ export class LiveQuery extends TxProcessor implements Client {
protected override async txMixin (tx: TxMixin<Doc, Doc>): Promise<TxResult> {
const hierarchy = this.client.getHierarchy()
for (const queries of this.queries) {
const isTx = hierarchy.isDerived(queries[0], core.class.Tx)
const isMixin = hierarchy.isDerived(tx.mixin, queries[0])
@ -433,16 +434,22 @@ export class LiveQuery extends TxProcessor implements Client {
// If query contains search we must check use fulltext
if (q.query.$search != null && q.query.$search.length > 0) {
const searchRefresh = await this.checkSearch(q, tx.objectId)
if (searchRefresh) return {}
if (searchRefresh) {
continue
}
} else {
const updatedDoc = q.result[pos]
if (updatedDoc.modifiedOn < tx.modifiedOn) {
await this.__updateMixinDoc(q, updatedDoc, tx)
const updateRefresh = await this.checkUpdatedDocMatch(q, updatedDoc)
if (updateRefresh) return {}
if (updateRefresh) {
continue
}
} else {
const currentRefresh = await this.getCurrentDoc(q, updatedDoc._id)
if (currentRefresh) return {}
if (currentRefresh) {
continue
}
}
}
await this.sort(q, tx)

View File

@ -15,30 +15,30 @@
<script lang="ts">
import { CalendarMode } from '@hcengineering/calendar-resources'
import calendar from '@hcengineering/calendar-resources/src/plugin'
import { EmployeeAccount } from '@hcengineering/contact'
import { employeeByIdStore } from '@hcengineering/contact-resources'
import { DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core'
import { Department, Staff } from '@hcengineering/hr'
import { createQuery, getClient, SpaceSelector } from '@hcengineering/presentation'
import {
Button,
IconBack,
IconForward,
Label,
deviceOptionsStore as deviceInfo,
TabList,
SearchEdit
} from '@hcengineering/ui'
import { createQuery, SpaceSelector } from '@hcengineering/presentation'
import type { TabItem } from '@hcengineering/ui'
import { Button, IconBack, IconForward, Label, SearchEdit, TabList } from '@hcengineering/ui'
import view from '@hcengineering/view'
import hr from '../plugin'
import ScheduleView from './ScheduleView.svelte'
import { EmployeeAccount } from '@hcengineering/contact'
import { employeeByIdStore } from '@hcengineering/contact-resources'
const hierarchy = getClient().getHierarchy()
const accountEmployee = $employeeByIdStore.get((getCurrentAccount() as EmployeeAccount).employee)
const accountStaff = accountEmployee !== undefined ? hierarchy.as(accountEmployee, hr.mixin.Staff) : undefined
let accountStaff: Staff | undefined
const accountStaffQ = createQuery()
let department = accountStaff !== undefined ? accountStaff.department : hr.ids.Head
$: if (accountEmployee !== undefined) {
accountStaffQ.query(hr.mixin.Staff, { _id: accountEmployee._id as Ref<Staff> }, (res) => {
accountStaff = res[0]
department = accountStaff !== undefined ? accountStaff.department : hr.ids.Head
})
}
let currentDate: Date = new Date()
let search = ''
@ -89,7 +89,6 @@
}).format(date)
}
$: twoRows = $deviceInfo.twoRows
const handleSelect = (result: any) => {
if (result.type === 'select') {
const res = result.detail

View File

@ -276,42 +276,44 @@
}
return map
}
let staffDepartmentMap = new Map()
$: getDepartmentsForEmployee(departmentStaff).then((res) => {
staffDepartmentMap = res
})
</script>
{#if departmentStaff.length}
{#await getDepartmentsForEmployee(departmentStaff) then staffDepartmentMap}
{#if mode === CalendarMode.Year}
<YearView {departmentStaff} {employeeRequests} {types} {currentDate} {holidays} {staffDepartmentMap} />
{:else if mode === CalendarMode.Month}
{#if display === 'chart'}
<MonthView
{departmentStaff}
{employeeRequests}
{types}
{startDate}
{endDate}
{editableList}
{currentDate}
{timeReports}
{holidays}
{department}
{departments}
{staffDepartmentMap}
/>
{:else if display === 'stats'}
<MonthTableView
{departmentStaff}
{employeeRequests}
{types}
{currentDate}
{timeReports}
{holidays}
{staffDepartmentMap}
{getHolidays}
/>
{/if}
{#if staffDepartmentMap.size > 0}
{#if mode === CalendarMode.Year}
<YearView {departmentStaff} {employeeRequests} {types} {currentDate} {holidays} {staffDepartmentMap} />
{:else if mode === CalendarMode.Month}
{#if display === 'chart'}
<MonthView
{departmentStaff}
{employeeRequests}
{types}
{startDate}
{endDate}
{editableList}
{currentDate}
{timeReports}
{holidays}
{department}
{departments}
{staffDepartmentMap}
/>
{:else if display === 'stats'}
<MonthTableView
{departmentStaff}
{employeeRequests}
{types}
{currentDate}
{timeReports}
{holidays}
{staffDepartmentMap}
{getHolidays}
/>
{/if}
{/await}
{/if}
{:else}
<div class="flex-center h-full w-full flex-grow fs-title">
<Label label={hr.string.NoEmployeesInDepartment} />

View File

@ -40,11 +40,16 @@ export async function addMember (client: TxOperations, employee?: Employee, valu
undefined,
(res?: boolean) => {
if (res === true && value !== undefined) {
void client.updateMixin(employee._id, employee._class, employee.space, hr.mixin.Staff, {
department: value._id
})
void client
.updateMixin(employee._id, employee._class, employee.space, hr.mixin.Staff, {
department: value._id
})
.then(() => {
resolve(null)
})
} else {
resolve(null)
}
resolve(null)
}
)
})

View File

@ -76,8 +76,8 @@
"FilteredViewName": "Filtered view name",
"Then": "Then",
"ShowPreviewOnClick": "Please click to show document index preview...",
"Shown": "Shown",
"Total": "Total",
"Shown": "showing {len} results {total, plural, =-1 {} other {of filtered # items}}",
"Total": "Total: {total, plural, other{#}}",
"ShowEmptyGroups": "Show empty groups",
"Overdue": "Overdue",
"Today": "Today",

View File

@ -72,8 +72,8 @@
"FilteredViewName": "Имя фильтрованного отображения",
"Then": "Затем",
"ShowPreviewOnClick": "Пожалуйста нажмите чтобы увидеть предпросмотр...",
"Shown": "Показано",
"Total": "Всего",
"Shown": "показано {len} результатов {total, plural, =-1 {} other {из #}}",
"Total": "Всего: {total, plural, other{#}}",
"ShowEmptyGroups": "Показывать пустые группы",
"Overdue": "Overdue",
"Today": "Сегодня",

View File

@ -18,7 +18,8 @@
import { getObjectValue, SortingOrder } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { createQuery, getClient, updateAttribute } from '@hcengineering/presentation'
import {
import ui, {
Button,
CheckBox,
Component,
getEventPositionElement,
@ -49,6 +50,8 @@
export let readonly = false
export let showFooter = false
export let totalQuery: DocumentQuery<Doc> | undefined = undefined
export let prefferedSorting: string = 'modifiedOn'
export let limit = 200
@ -75,7 +78,8 @@
let userSorting = false
let objects: Doc[] = []
let total: number
let gtotal: number = 0
let total: number = 0
let objectsRecieved = false
const refs: HTMLElement[] = []
@ -109,6 +113,7 @@
(result) => {
objects = result
total = result.total === -1 ? 0 : result.total
objectsRecieved = true
if (sortingFunction !== undefined) {
const sf = sortingFunction
@ -215,6 +220,20 @@
}
let width: number
const totalQueryQ = createQuery()
$: totalQueryQ.query(
_class,
totalQuery ?? query ?? {},
(result) => {
gtotal = result.total
},
{
lookup,
limit: 1,
total: true
}
)
</script>
{#await buildModel({ client, _class, keys: config, lookup })}
@ -362,17 +381,26 @@
<div class="space" />
<div class="footer" style="width: {width}px;">
<div class="content" class:padding={showNotification || enableChecking}>
<Label label={view.string.Total} />: {total}
{#if objects.length > 0 && objects.length < total}
<span class="select-text">
<Label label={view.string.Total} params={{ total: gtotal }} />
</span>
{#if objects.length > 0 && objects.length < gtotal}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="cursor-pointer ml-2"
<span class="select-text ml-2">
<Label
label={view.string.Shown}
params={{ total: objects.length === total ? -1 : total, len: objects.length }}
/>
</span>
<Button
label={ui.string.ShowMore}
kind={'transparent'}
size={'small'}
on:click={() => {
limit = limit + 100
}}
>
<Label label={view.string.Shown} />: {objects.length}
</div>
/>
{/if}
</div>
</div>

View File

@ -26,6 +26,7 @@
export let _class: Ref<Class<Doc>>
export let query: DocumentQuery<Doc>
export let totalQuery: DocumentQuery<Doc> | undefined = undefined
export let showNotification: boolean = false
export let options: FindOptions<Doc> | undefined = undefined
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
@ -91,6 +92,7 @@
config={_config}
{options}
{query}
{totalQuery}
{showNotification}
{baseMenuClass}
{loadingProps}

View File

@ -209,7 +209,8 @@
viewOptionsConfig: viewlet.viewOptions?.other,
createItemDialog: createComponent,
createItemLabel: createLabel,
query: resultQuery
query: resultQuery,
totalQuery: query
}}
/>
{/if}

View File

@ -417,7 +417,10 @@ abstract class MongoAdapterBase implements DbAdapter {
}
})
const domain = this.hierarchy.getDomain(clazz)
const cursor = this.db.collection(domain).aggregate(pipeline)
const cursor = this.db.collection(domain).aggregate(pipeline, {
checkKeys: false,
enableUtf8Validation: false
})
cursor.maxTimeMS(30000)
const res = (await cursor.toArray())[0]
const result = res.results as WithLookup<T>[]

View File

@ -83,7 +83,7 @@ export function protoDeserialize (data: any, binary: boolean): any {
if (!binary) {
return JSON.parse(data, receiver)
}
return packr.unpack(new Uint8Array(data))
return packr.unpack(new Uint8Array(replacer('', data)))
}
/**
@ -92,6 +92,9 @@ export function protoDeserialize (data: any, binary: boolean): any {
* @returns
*/
export function serialize (object: Request<any> | Response<any>, binary: boolean): any {
if ((object as any).result !== undefined) {
;(object as any).result = replacer('result', (object as any).result)
}
return protoSerialize(object, binary)
}
@ -101,7 +104,11 @@ export function serialize (object: Request<any> | Response<any>, binary: boolean
* @returns
*/
export function readResponse<D> (response: any, binary: boolean): Response<D> {
return protoDeserialize(response, binary)
const data = protoDeserialize(response, binary)
if (data.result !== undefined) {
data.result = receiver('result', data.result)
}
return data
}
function replacer (key: string, value: any): any {