mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-18 06:13:52 +00:00
Model lookup (#259)
Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
parent
56deb3a4b0
commit
771188e72f
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,9 @@ export const DOMAIN_STATE = 'state' as Domain
|
|||||||
|
|
||||||
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
|
@Model(core.class.Space, core.class.Doc, DOMAIN_MODEL)
|
||||||
export class TSpace extends TDoc implements Space {
|
export class TSpace extends TDoc implements Space {
|
||||||
|
@Prop(TypeString(), 'Name' as IntlString)
|
||||||
name!: string
|
name!: string
|
||||||
|
|
||||||
description!: string
|
description!: string
|
||||||
private!: boolean
|
private!: boolean
|
||||||
members!: Arr<Ref<Account>>
|
members!: Arr<Ref<Account>>
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"deep-equal": "^2.0.5",
|
"deep-equal": "^2.0.5",
|
||||||
"@anticrm/panel": "~0.6.0",
|
"@anticrm/panel": "~0.6.0",
|
||||||
"@anticrm/chunter-resources": "~0.6.0",
|
"@anticrm/chunter-resources": "~0.6.0",
|
||||||
"@anticrm/view": "~0.6.0"
|
"@anticrm/view": "~0.6.0",
|
||||||
|
"@anticrm/view-resources": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,9 @@
|
|||||||
import Edit from './icons/Edit.svelte'
|
import Edit from './icons/Edit.svelte'
|
||||||
import SocialEditor from './SocialEditor.svelte'
|
import SocialEditor from './SocialEditor.svelte'
|
||||||
import AttributesBar from './AttributesBar.svelte'
|
import AttributesBar from './AttributesBar.svelte'
|
||||||
|
import { TableView } from '@anticrm/view-resources'
|
||||||
|
|
||||||
import chunter from '@anticrm/chunter'
|
import core from '@anticrm/core'
|
||||||
|
|
||||||
import recruit from '../plugin'
|
import recruit from '../plugin'
|
||||||
import { combineName, formatName, getFirstName, getLastName } from '@anticrm/contact'
|
import { combineName, formatName, getFirstName, getLastName } from '@anticrm/contact'
|
||||||
@ -88,6 +89,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Applications
|
||||||
|
|
||||||
|
<TableView
|
||||||
|
_class={recruit.class.Applicant}
|
||||||
|
config={['$lookup.candidate', '$lookup.state', '$lookup.candidate.city', '$lookup.space.name']}
|
||||||
|
options={
|
||||||
|
{
|
||||||
|
lookup: {
|
||||||
|
candidate: recruit.class.Candidate,
|
||||||
|
state: core.class.State,
|
||||||
|
space: core.class.Space
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
search=""
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="attachments">
|
<div class="attachments">
|
||||||
<Attachments objectId={object._id} _class={object._class} space={object.space} {object}/>
|
<Attachments objectId={object._id} _class={object._class} space={object.space} {object}/>
|
||||||
</div>
|
</div>
|
||||||
|
24
plugins/view-resources/src/components/SpacePresenter.svelte
Normal file
24
plugins/view-resources/src/components/SpacePresenter.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Space } from '@anticrm/core'
|
||||||
|
|
||||||
|
export let value: Space
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{value.name}
|
@ -38,7 +38,7 @@
|
|||||||
let objects: Doc[]
|
let objects: Doc[]
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
$: query.query(_class, search === '' ? { space } : { $search: search }, result => { objects = result }, { sort: { [sortKey]: sortOrder }, ...options })
|
$: query.query(_class, search === '' ? (space ? { space } : {}) : { $search: search }, result => { objects = result }, { sort: { [sortKey]: sortOrder }, ...options })
|
||||||
|
|
||||||
function getValue(doc: Doc, key: string): any {
|
function getValue(doc: Doc, key: string): any {
|
||||||
if (key.length === 0)
|
if (key.length === 0)
|
||||||
|
@ -25,6 +25,8 @@ import KanbanView from './components/KanbanView.svelte'
|
|||||||
|
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
|
|
||||||
|
export { TableView }
|
||||||
|
|
||||||
async function Delete(object: Doc): Promise<void> {
|
async function Delete(object: Doc): Promise<void> {
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
await client.removeDoc(object._class, object.space, object._id)
|
await client.removeDoc(object._class, object.space, object._id)
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { ServerStorage, Domain, Tx, TxCUD, Doc, Ref, Class, DocumentQuery, FindResult, FindOptions, Storage, TxBulkWrite } from '@anticrm/core'
|
import type { ServerStorage, Domain, Tx, TxCUD, Doc, Ref, Class, DocumentQuery, FindResult, FindOptions, Storage, TxBulkWrite } from '@anticrm/core'
|
||||||
import core, { Hierarchy, DOMAIN_TX } from '@anticrm/core'
|
import core, { Hierarchy, DOMAIN_TX, ModelDb } from '@anticrm/core'
|
||||||
import type { FullTextAdapterFactory, FullTextAdapter } from './types'
|
import type { FullTextAdapterFactory, FullTextAdapter } from './types'
|
||||||
import { FullTextIndex } from './fulltext'
|
import { FullTextIndex } from './fulltext'
|
||||||
import { Triggers } from './triggers'
|
import { Triggers } from './triggers'
|
||||||
@ -40,7 +40,7 @@ export interface TxAdapter extends DbAdapter {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export type DbAdapterFactory = (hierarchy: Hierarchy, url: string, db: string) => Promise<DbAdapter>
|
export type DbAdapterFactory = (hierarchy: Hierarchy, url: string, db: string, modelDb: ModelDb) => Promise<DbAdapter>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -73,7 +73,8 @@ class TServerStorage implements ServerStorage {
|
|||||||
private readonly adapters: Map<string, DbAdapter>,
|
private readonly adapters: Map<string, DbAdapter>,
|
||||||
private readonly hierarchy: Hierarchy,
|
private readonly hierarchy: Hierarchy,
|
||||||
private readonly triggers: Triggers,
|
private readonly triggers: Triggers,
|
||||||
fulltextAdapter: FullTextAdapter
|
fulltextAdapter: FullTextAdapter,
|
||||||
|
private readonly modelDb: ModelDb
|
||||||
) {
|
) {
|
||||||
this.fulltext = new FullTextIndex(hierarchy, fulltextAdapter, this)
|
this.fulltext = new FullTextIndex(hierarchy, fulltextAdapter, this)
|
||||||
}
|
}
|
||||||
@ -125,24 +126,23 @@ class TServerStorage implements ServerStorage {
|
|||||||
// maintain hiearachy and triggers
|
// maintain hiearachy and triggers
|
||||||
this.hierarchy.tx(tx)
|
this.hierarchy.tx(tx)
|
||||||
await this.triggers.tx(tx)
|
await this.triggers.tx(tx)
|
||||||
return []
|
await this.modelDb.tx(tx)
|
||||||
} else {
|
|
||||||
// store object
|
|
||||||
await this.routeTx(tx)
|
|
||||||
// invoke triggers and store derived objects
|
|
||||||
const derived = await this.triggers.apply(tx.modifiedBy, tx, this.findAll.bind(this), this.hierarchy)
|
|
||||||
for (const tx of derived) {
|
|
||||||
await this.routeTx(tx)
|
|
||||||
}
|
|
||||||
// index object
|
|
||||||
await this.fulltext.tx(tx)
|
|
||||||
// index derived objects
|
|
||||||
for (const tx of derived) {
|
|
||||||
await this.fulltext.tx(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return derived
|
|
||||||
}
|
}
|
||||||
|
// store object
|
||||||
|
await this.routeTx(tx)
|
||||||
|
// invoke triggers and store derived objects
|
||||||
|
const derived = await this.triggers.apply(tx.modifiedBy, tx, this.findAll.bind(this), this.hierarchy)
|
||||||
|
for (const tx of derived) {
|
||||||
|
await this.routeTx(tx)
|
||||||
|
}
|
||||||
|
// index object
|
||||||
|
await this.fulltext.tx(tx)
|
||||||
|
// index derived objects
|
||||||
|
for (const tx of derived) {
|
||||||
|
await this.fulltext.tx(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return derived
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +153,11 @@ export async function createServerStorage (conf: DbConfiguration): Promise<Serve
|
|||||||
const hierarchy = new Hierarchy()
|
const hierarchy = new Hierarchy()
|
||||||
const triggers = new Triggers()
|
const triggers = new Triggers()
|
||||||
const adapters = new Map<string, DbAdapter>()
|
const adapters = new Map<string, DbAdapter>()
|
||||||
|
const modelDb = new ModelDb(hierarchy)
|
||||||
|
|
||||||
for (const key in conf.adapters) {
|
for (const key in conf.adapters) {
|
||||||
const adapterConf = conf.adapters[key]
|
const adapterConf = conf.adapters[key]
|
||||||
adapters.set(key, await adapterConf.factory(hierarchy, adapterConf.url, conf.workspace))
|
adapters.set(key, await adapterConf.factory(hierarchy, adapterConf.url, conf.workspace, modelDb))
|
||||||
}
|
}
|
||||||
|
|
||||||
const txAdapter = adapters.get(conf.domains[DOMAIN_TX]) as TxAdapter
|
const txAdapter = adapters.get(conf.domains[DOMAIN_TX]) as TxAdapter
|
||||||
@ -171,11 +172,15 @@ export async function createServerStorage (conf: DbConfiguration): Promise<Serve
|
|||||||
await triggers.tx(tx)
|
await triggers.tx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const tx of model) {
|
||||||
|
await modelDb.tx(tx)
|
||||||
|
}
|
||||||
|
|
||||||
for (const [, adapter] of adapters) {
|
for (const [, adapter] of adapters) {
|
||||||
await adapter.init(model)
|
await adapter.init(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fulltextAdapter = await conf.fulltextAdapter.factory(conf.fulltextAdapter.url, conf.workspace)
|
const fulltextAdapter = await conf.fulltextAdapter.factory(conf.fulltextAdapter.url, conf.workspace)
|
||||||
|
|
||||||
return new TServerStorage(conf.domains, conf.defaultAdapter, adapters, hierarchy, triggers, fulltextAdapter)
|
return new TServerStorage(conf.domains, conf.defaultAdapter, adapters, hierarchy, triggers, fulltextAdapter, modelDb)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import type { Tx, Ref, Doc, Class, DocumentQuery, FindResult, FindOptions, TxCreateDoc, TxUpdateDoc, TxMixin, TxPutBag, TxRemoveDoc } from '@anticrm/core'
|
import type { Tx, Ref, Doc, Class, DocumentQuery, FindResult, FindOptions, TxCreateDoc, TxUpdateDoc, TxMixin, TxPutBag, TxRemoveDoc } from '@anticrm/core'
|
||||||
import core, { DOMAIN_TX, SortingOrder, TxProcessor, Hierarchy, isOperator } from '@anticrm/core'
|
import core, { DOMAIN_TX, DOMAIN_MODEL, SortingOrder, TxProcessor, Hierarchy, isOperator, ModelDb } from '@anticrm/core'
|
||||||
|
|
||||||
import type { DbAdapter, TxAdapter } from '@anticrm/server-core'
|
import type { DbAdapter, TxAdapter } from '@anticrm/server-core'
|
||||||
|
|
||||||
@ -27,7 +27,8 @@ function translateDoc (doc: Doc): Document {
|
|||||||
abstract class MongoAdapterBase extends TxProcessor {
|
abstract class MongoAdapterBase extends TxProcessor {
|
||||||
constructor (
|
constructor (
|
||||||
protected readonly db: Db,
|
protected readonly db: Db,
|
||||||
protected readonly hierarchy: Hierarchy
|
protected readonly hierarchy: Hierarchy,
|
||||||
|
protected readonly modelDb: ModelDb
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@ -63,13 +64,16 @@ abstract class MongoAdapterBase extends TxProcessor {
|
|||||||
const lookups = options.lookup as any
|
const lookups = options.lookup as any
|
||||||
for (const key in lookups) {
|
for (const key in lookups) {
|
||||||
const clazz = lookups[key]
|
const clazz = lookups[key]
|
||||||
const step = {
|
const domain = this.hierarchy.getDomain(clazz)
|
||||||
from: this.hierarchy.getDomain(clazz),
|
if (domain !== DOMAIN_MODEL) {
|
||||||
localField: key,
|
const step = {
|
||||||
foreignField: '_id',
|
from: domain,
|
||||||
as: key + '_lookup'
|
localField: key,
|
||||||
|
foreignField: '_id',
|
||||||
|
as: key + '_lookup'
|
||||||
|
}
|
||||||
|
pipeline.push({ $lookup: step })
|
||||||
}
|
}
|
||||||
pipeline.push({ $lookup: step })
|
|
||||||
}
|
}
|
||||||
const domain = this.hierarchy.getDomain(clazz)
|
const domain = this.hierarchy.getDomain(clazz)
|
||||||
const cursor = this.db.collection(domain).aggregate(pipeline)
|
const cursor = this.db.collection(domain).aggregate(pipeline)
|
||||||
@ -78,8 +82,14 @@ abstract class MongoAdapterBase extends TxProcessor {
|
|||||||
const object = row as any
|
const object = row as any
|
||||||
object.$lookup = {}
|
object.$lookup = {}
|
||||||
for (const key in lookups) {
|
for (const key in lookups) {
|
||||||
const arr = object[key + '_lookup']
|
const clazz = lookups[key]
|
||||||
object.$lookup[key] = arr[0]
|
const domain = this.hierarchy.getDomain(clazz)
|
||||||
|
if (domain !== DOMAIN_MODEL) {
|
||||||
|
const arr = object[key + '_lookup']
|
||||||
|
object.$lookup[key] = arr[0]
|
||||||
|
} else {
|
||||||
|
object.$lookup[key] = this.modelDb.getObject(object[key])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -214,19 +224,19 @@ class MongoTxAdapter extends MongoAdapterBase implements TxAdapter {
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function createMongoAdapter (hierarchy: Hierarchy, url: string, dbName: string): Promise<DbAdapter> {
|
export async function createMongoAdapter (hierarchy: Hierarchy, url: string, dbName: string, modelDb: ModelDb): Promise<DbAdapter> {
|
||||||
const client = new MongoClient(url)
|
const client = new MongoClient(url)
|
||||||
await client.connect()
|
await client.connect()
|
||||||
const db = client.db(dbName)
|
const db = client.db(dbName)
|
||||||
return new MongoAdapter(db, hierarchy)
|
return new MongoAdapter(db, hierarchy, modelDb)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function createMongoTxAdapter (hierarchy: Hierarchy, url: string, dbName: string): Promise<TxAdapter> {
|
export async function createMongoTxAdapter (hierarchy: Hierarchy, url: string, dbName: string, modelDb: ModelDb): Promise<TxAdapter> {
|
||||||
const client = new MongoClient(url)
|
const client = new MongoClient(url)
|
||||||
await client.connect()
|
await client.connect()
|
||||||
const db = client.db(dbName)
|
const db = client.db(dbName)
|
||||||
return new MongoTxAdapter(db, hierarchy)
|
return new MongoTxAdapter(db, hierarchy, modelDb)
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,28 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { DOMAIN_TX } from '@anticrm/core'
|
import { Class, Doc, DocumentQuery, DOMAIN_MODEL, DOMAIN_TX, FindOptions, FindResult, Hierarchy, ModelDb, Ref, Tx } from '@anticrm/core'
|
||||||
import { start as startJsonRpc } from '@anticrm/server-ws'
|
import { start as startJsonRpc } from '@anticrm/server-ws'
|
||||||
import { createMongoAdapter, createMongoTxAdapter } from '@anticrm/mongo'
|
import { createMongoAdapter, createMongoTxAdapter } from '@anticrm/mongo'
|
||||||
import { createElasticAdapter } from '@anticrm/elastic'
|
import { createElasticAdapter } from '@anticrm/elastic'
|
||||||
import { createServerStorage } from '@anticrm/server-core'
|
import { createServerStorage } from '@anticrm/server-core'
|
||||||
import type { DbConfiguration } from '@anticrm/server-core'
|
import type { DbConfiguration, DbAdapter } from '@anticrm/server-core'
|
||||||
|
|
||||||
import { addLocation } from '@anticrm/platform'
|
import { addLocation } from '@anticrm/platform'
|
||||||
import { serverChunterId } from '@anticrm/server-chunter'
|
import { serverChunterId } from '@anticrm/server-chunter'
|
||||||
import { serverRecruitId } from '@anticrm/server-recruit'
|
import { serverRecruitId } from '@anticrm/server-recruit'
|
||||||
import { serverViewId } from '@anticrm/server-view'
|
import { serverViewId } from '@anticrm/server-view'
|
||||||
|
|
||||||
|
class NullDbAdapter implements DbAdapter {
|
||||||
|
async init (model: Tx[]): Promise<void> {}
|
||||||
|
async findAll <T extends Doc>(_class: Ref<Class<T>>, query: DocumentQuery<T>, options?: FindOptions<T> | undefined): Promise<FindResult<T>> { return [] }
|
||||||
|
async tx (tx: Tx): Promise<void> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createNullAdapter (hierarchy: Hierarchy, url: string, db: string, modelDb: ModelDb): Promise<DbAdapter> {
|
||||||
|
return new NullDbAdapter()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -37,7 +47,8 @@ export async function start (dbUrl: string, fullTextUrl: string, port: number, h
|
|||||||
startJsonRpc((workspace: string) => {
|
startJsonRpc((workspace: string) => {
|
||||||
const conf: DbConfiguration = {
|
const conf: DbConfiguration = {
|
||||||
domains: {
|
domains: {
|
||||||
[DOMAIN_TX]: 'MongoTx'
|
[DOMAIN_TX]: 'MongoTx',
|
||||||
|
[DOMAIN_MODEL]: 'Null'
|
||||||
},
|
},
|
||||||
defaultAdapter: 'Mongo',
|
defaultAdapter: 'Mongo',
|
||||||
adapters: {
|
adapters: {
|
||||||
@ -48,6 +59,10 @@ export async function start (dbUrl: string, fullTextUrl: string, port: number, h
|
|||||||
Mongo: {
|
Mongo: {
|
||||||
factory: createMongoAdapter,
|
factory: createMongoAdapter,
|
||||||
url: dbUrl
|
url: dbUrl
|
||||||
|
},
|
||||||
|
Null: {
|
||||||
|
factory: createNullAdapter,
|
||||||
|
url: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fulltextAdapter: {
|
fulltextAdapter: {
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user