@mikro-orm/sql 7.2.2-dev.8 → 7.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -850,9 +850,13 @@ export class AbstractSqlDriver extends DatabaseDriver {
850
850
  props.forEach(prop => {
851
851
  // For STI with conflicting fieldNames, use discriminator to determine which field gets value
852
852
  if (prop.stiFieldNames && prop.stiFieldNameMap && meta.discriminatorColumn) {
853
- const activeField = prop.stiFieldNameMap[row[meta.discriminatorColumn]];
853
+ // the map misses subtypes whose props were not initialized yet during discovery (root column owner, grandchildren)
854
+ const discriminator = row[meta.discriminatorColumn];
855
+ const childClass = meta.discriminatorMap?.[discriminator];
856
+ const activeField = prop.stiFieldNameMap[discriminator] ??
857
+ (childClass && this.metadata.find(childClass)?.properties[prop.name]?.fieldNames[0]);
854
858
  for (const field of prop.stiFieldNames) {
855
- params.push(field === activeField ? row[prop.name] : null);
859
+ params.push(field === activeField ? (row[prop.name] ?? null) : null);
856
860
  keys.push('?');
857
861
  }
858
862
  return;
@@ -1809,7 +1813,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
1809
1813
  if (!this.platform.shouldHaveColumn(prop, populate, exclude)) {
1810
1814
  return false;
1811
1815
  }
1812
- if (!fields || fields.includes('*') || prop.primary || meta.root.discriminatorColumn === prop.name) {
1816
+ if (!fields ||
1817
+ fields.includes('*') ||
1818
+ prop.primary ||
1819
+ meta.root.discriminatorColumn === prop.name ||
1820
+ meta.root.targetKeys?.includes(prop.name)) {
1813
1821
  return true;
1814
1822
  }
1815
1823
  return fields.some(f => f === prop.name || f.toString().startsWith(prop.name + '.'));
@@ -1865,11 +1873,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
1865
1873
  const tptMeta = this.metadata.get(targetMeta.class);
1866
1874
  this.addTPTPolymorphicJoinsForRelation(qb, tptMeta, tableAlias, fields);
1867
1875
  }
1868
- if (ref) {
1869
- // For filter :ref hints, schedule filter check for each target (no field selection)
1870
- qb.scheduleFilterCheck(targetPath);
1871
- }
1872
- else {
1876
+ // target filters apply per target, both for filter :ref hints and populated relations
1877
+ qb.scheduleFilterCheck(targetPath);
1878
+ if (!ref) {
1873
1879
  // Select fields from each target table
1874
1880
  fields.push(...this.getFieldsForJoinedLoad(qb, targetMeta, {
1875
1881
  ...options,
@@ -2469,6 +2475,8 @@ export class AbstractSqlDriver extends DatabaseDriver {
2469
2475
  }
2470
2476
  if (!options.fields.includes('*') && !options.fields.includes(`${qb.alias}.*`)) {
2471
2477
  ret.unshift(...meta.primaryKeys.filter(pk => !options.fields.includes(pk)));
2478
+ // `targetKey` values identify the entity like its PK, references resolve it by them
2479
+ ret.push(...(meta.root.targetKeys ?? []).filter(key => meta.properties[key] && !options.fields.includes(key)));
2472
2480
  }
2473
2481
  if (meta.root.inheritanceType === 'sti' &&
2474
2482
  !options.fields.includes(`${qb.alias}.${meta.root.discriminatorColumn}`)) {
@@ -1,4 +1,4 @@
1
- import { EntityManager, raw, Utils, } from '@mikro-orm/core';
1
+ import { EntityManager, QueryFlag, raw, Utils, } from '@mikro-orm/core';
2
2
  import { MikroKyselyPlugin } from './plugin/index.js';
3
3
  /**
4
4
  * @inheritDoc
@@ -87,6 +87,7 @@ export class SqlEntityManager extends EntityManager {
87
87
  // match `em.count()` semantics: an active transaction always wins over the requested connection type
88
88
  const connectionType = em.getTransactionContext() ? 'write' : options.connectionType;
89
89
  const qb = em.createQueryBuilder(meta.class, undefined, connectionType);
90
+ qb.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
90
91
  qb
91
92
  .select([...fields, raw('count(*) as cnt')])
92
93
  .where(where)
@@ -419,7 +419,11 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
419
419
  if ((col.udt_name === 'geometry' || col.udt_name === 'geography') && col.format_type) {
420
420
  type = col.format_type;
421
421
  }
422
- if (type === 'vector' && col.length == null && col.custom_length != null && col.custom_length !== -1) {
422
+ // pgvector stores the dimension in atttypmod for all three of its vector types
423
+ if (['vector', 'halfvec', 'sparsevec'].includes(type) &&
424
+ col.length == null &&
425
+ col.custom_length != null &&
426
+ col.custom_length !== -1) {
423
427
  col.length = col.custom_length;
424
428
  }
425
429
  if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.2-dev.8",
3
+ "version": "7.2.2",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -50,10 +50,10 @@
50
50
  "kysely": "0.29.6"
51
51
  },
52
52
  "devDependencies": {
53
- "@mikro-orm/core": "^7.2.1"
53
+ "@mikro-orm/core": "^7.2.2"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.2-dev.8"
56
+ "@mikro-orm/core": "7.2.2"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -92,6 +92,7 @@ export declare class MikroTransformer extends OperationNodeTransformer {
92
92
  * This is called for SELECT queries when columnNamingStrategy is 'property'
93
93
  */
94
94
  transformResult(rows: Record<string, any>[] | undefined, entityMap: Map<string, EntityMetadata>): Record<string, any>[] | undefined;
95
+ private getResultMaps;
95
96
  buildGlobalFieldMap(entityMap: Map<string, EntityMetadata>): Record<string, EntityProperty>;
96
97
  buildGlobalRelationFieldMap(entityMap: Map<string, EntityMetadata>): Record<string, string>;
97
98
  /**
@@ -108,6 +109,6 @@ export declare class MikroTransformer extends OperationNodeTransformer {
108
109
  /**
109
110
  * Transform a single row by mapping column names to property names
110
111
  */
111
- transformRow(row: Record<string, any>, fieldToPropertyMap: Record<string, EntityProperty>, relationFieldMap: Record<string, string>): Record<string, any>;
112
+ transformRow(row: Record<string, any>, fieldEntries: [string, EntityProperty][], relationEntries: [string, string][]): Record<string, any>;
112
113
  prepareOutputValue(prop: EntityProperty | undefined, value: unknown): unknown;
113
114
  }
@@ -1,5 +1,12 @@
1
1
  import { ReferenceKind, isRaw, } from '@mikro-orm/core';
2
2
  import { AliasNode, ColumnNode, ColumnUpdateNode, IdentifierNode, OperationNodeTransformer, PrimitiveValueListNode, RawNode, ReferenceNode, SchemableIdentifierNode, SelectAllNode, SelectionNode, TableNode, ValueListNode, ValueNode, ValuesNode, } from 'kysely';
3
+ /** Past this many query shapes the cache of one metadata storage starts over, so generated aliases cannot grow it. */
4
+ const RESULT_MAPS_LIMIT = 1000;
5
+ /**
6
+ * Result maps per query shape (the aliases and entities of its entity map). They are derived from metadata alone, so
7
+ * they are built once and shared by every transformer, as `getKysely()` creates a new one per call.
8
+ */
9
+ const resultMapsCache = new WeakMap();
3
10
  const EXPANDABLE_KINDS = new Set([
4
11
  ReferenceKind.SCALAR,
5
12
  ReferenceKind.EMBEDDED,
@@ -884,12 +891,7 @@ export class MikroTransformer extends OperationNodeTransformer {
884
891
  if (byEntity) {
885
892
  return byEntity;
886
893
  }
887
- const allMetadata = Array.from(this.#metadata);
888
- const byTable = allMetadata.find(m => m.tableName === name);
889
- if (byTable) {
890
- return byTable;
891
- }
892
- return undefined;
894
+ return this.#metadata.getByTableName(name);
893
895
  }
894
896
  /**
895
897
  * Transform result rows by mapping database column names to property names
@@ -906,11 +908,35 @@ export class MikroTransformer extends OperationNodeTransformer {
906
908
  if (entityMap.size === 0) {
907
909
  return rows;
908
910
  }
909
- // Build a global mapping from database field names to property objects
910
- const fieldToPropertyMap = this.buildGlobalFieldMap(entityMap);
911
- const relationFieldMap = this.buildGlobalRelationFieldMap(entityMap);
911
+ // The global mapping from database field names to property objects, built once per query shape
912
+ const { fields, relations } = this.getResultMaps(entityMap);
912
913
  // Transform each row
913
- return rows.map(row => this.transformRow(row, fieldToPropertyMap, relationFieldMap));
914
+ return rows.map(row => this.transformRow(row, fields, relations));
915
+ }
916
+ getResultMaps(entityMap) {
917
+ let byShape = resultMapsCache.get(this.#metadata);
918
+ if (!byShape) {
919
+ byShape = new Map();
920
+ resultMapsCache.set(this.#metadata, byShape);
921
+ }
922
+ let shape = '';
923
+ for (const [alias, meta] of entityMap) {
924
+ shape += `${alias}\0${meta._id}\0`;
925
+ }
926
+ const cached = byShape.get(shape);
927
+ if (cached && Array.from(entityMap.values()).every((meta, i) => meta.props === cached.props[i])) {
928
+ return cached;
929
+ }
930
+ if (byShape.size >= RESULT_MAPS_LIMIT) {
931
+ byShape.clear();
932
+ }
933
+ const maps = {
934
+ props: Array.from(entityMap.values(), meta => meta.props),
935
+ fields: Object.entries(this.buildGlobalFieldMap(entityMap)),
936
+ relations: Object.entries(this.buildGlobalRelationFieldMap(entityMap)),
937
+ };
938
+ byShape.set(shape, maps);
939
+ return maps;
914
940
  }
915
941
  buildGlobalFieldMap(entityMap) {
916
942
  const map = {};
@@ -986,10 +1012,10 @@ export class MikroTransformer extends OperationNodeTransformer {
986
1012
  /**
987
1013
  * Transform a single row by mapping column names to property names
988
1014
  */
989
- transformRow(row, fieldToPropertyMap, relationFieldMap) {
1015
+ transformRow(row, fieldEntries, relationEntries) {
990
1016
  const transformed = { ...row };
991
1017
  // First pass: map regular fields from fieldName to propertyName and convert values
992
- for (const [fieldName, prop] of Object.entries(fieldToPropertyMap)) {
1018
+ for (const [fieldName, prop] of fieldEntries) {
993
1019
  if (!(fieldName in transformed)) {
994
1020
  continue;
995
1021
  }
@@ -1011,7 +1037,7 @@ export class MikroTransformer extends OperationNodeTransformer {
1011
1037
  // Second pass: handle relation fields
1012
1038
  // Only run if columnNamingStrategy is 'property', as we don't want to rename FKs otherwise
1013
1039
  if (this.#options.columnNamingStrategy === 'property') {
1014
- for (const [fieldName, relationPropertyName] of Object.entries(relationFieldMap)) {
1040
+ for (const [fieldName, relationPropertyName] of relationEntries) {
1015
1041
  if (fieldName in transformed && !(relationPropertyName in transformed)) {
1016
1042
  // Move the foreign key value to the relation property name
1017
1043
  transformed[relationPropertyName] = transformed[fieldName];
@@ -127,7 +127,7 @@ export declare class NativeQueryBuilder implements Subquery {
127
127
  as(alias: string): this;
128
128
  toRaw(): RawQueryFragment;
129
129
  protected compileSelect(): void;
130
- /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */
130
+ /** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
131
131
  protected needsCountSubquery(): boolean;
132
132
  protected getFields(countSubquery?: boolean): string;
133
133
  protected compileInsert(): void;
@@ -330,12 +330,12 @@ export class NativeQueryBuilder {
330
330
  this.parts.push(`)${asKeyword}${this.quote('dcnt')}`);
331
331
  }
332
332
  }
333
- /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */
333
+ /** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
334
334
  needsCountSubquery() {
335
335
  return (this.type === QueryType.COUNT &&
336
336
  !!this.options.distinct &&
337
- this.options.select.length > 1 &&
338
- !this.platform.supportsMultiColumnCountDistinct());
337
+ ((this.options.select.length > 1 && !this.platform.supportsMultiColumnCountDistinct()) ||
338
+ !!this.options.distinctOn));
339
339
  }
340
340
  getFields(countSubquery) {
341
341
  if (!this.options.select || this.options.select.length === 0) {
@@ -186,8 +186,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
186
186
  const operator = Utils.isObject(payload) &&
187
187
  Utils.getObjectQueryKeys(payload).every(k => {
188
188
  if (k === '$not' && Utils.isPlainObject(payload[k])) {
189
- // $not wrapping non-operator conditions (entity props) should be inlined
190
- return Utils.getObjectQueryKeys(payload[k]).every(ik => Utils.isOperator(ik, false));
189
+ // $not wrapping non-operator conditions (entity props) or an empty one should be inlined
190
+ const inner = Utils.getObjectQueryKeys(payload[k]);
191
+ return inner.length > 0 && inner.every(ik => Utils.isOperator(ik, false));
191
192
  }
192
193
  return Utils.isOperator(k, false);
193
194
  });
@@ -218,9 +219,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
218
219
  }
219
220
  else if (k === '$not' &&
220
221
  Utils.isPlainObject(payload[k]) &&
221
- Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false))) {
222
- // $not wraps entity conditions (from auto-join), inline at current level
223
- this.inlineCondition(k, o, payload[k]);
222
+ (!Utils.hasObjectKeys(payload[k]) ||
223
+ Utils.getObjectQueryKeys(payload[k]).some(ik => !Utils.isOperator(ik, false)))) {
224
+ // $not wraps entity conditions (from auto-join) or an empty one, inline at current level with the same key mapping
225
+ const inner = {};
226
+ this.inlineChildPayload(inner, payload[k], field, alias, childAlias);
227
+ this.inlineCondition(k, o, inner);
224
228
  }
225
229
  else if (Utils.isOperator(k, false)) {
226
230
  const tmp = payload[k];
@@ -291,6 +295,13 @@ export class ObjectCriteriaNode extends CriteriaNode {
291
295
  });
292
296
  const primaryKeys = knownKey &&
293
297
  keys.every(key => {
298
+ if (key === '$not') {
299
+ // e.g. `{ author: { $not: { id: 1 } } }` compares the FK column, relation PKs still need the join
300
+ const childPayload = this.payload[key].payload;
301
+ return (Utils.isPlainObject(childPayload) &&
302
+ Utils.getObjectQueryKeys(childPayload).every(k => meta.primaryKeys.includes(k) &&
303
+ meta.properties[k].kind === ReferenceKind.SCALAR));
304
+ }
294
305
  if (typeof key !== 'string' || !meta.primaryKeys.includes(key)) {
295
306
  return false;
296
307
  }
@@ -320,7 +331,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
320
331
  else {
321
332
  const prev = qb.state.fields?.slice();
322
333
  const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
323
- const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
334
+ // ordering must not drop rows whose polymorphic relation points to another target
335
+ const orderByPolymorphic = this.prop.polymorphic && options?.type === 'orderBy';
336
+ const joinType = toOneProperty && !this.prop.nullable && !orderByPolymorphic ? JoinType.innerJoin : JoinType.leftJoin;
324
337
  qb[method](field, nestedAlias, undefined, joinType, path);
325
338
  if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
326
339
  qb.state.fields = prev;
@@ -257,7 +257,11 @@ export class QueryBuilder {
257
257
  if (field) {
258
258
  this.#state.fields = Utils.asArray(field);
259
259
  }
260
- else if (distinct || this.hasToManyJoins()) {
260
+ else if (this.#state.distinctOn) {
261
+ this.#state.fields = this.#state.distinctOn;
262
+ distinct = true;
263
+ }
264
+ else if (distinct || this.#state.flags.has(QueryFlag.DISTINCT) || this.hasToManyJoins()) {
261
265
  this.#state.fields = this.mainAlias.meta.primaryKeys;
262
266
  }
263
267
  else {
@@ -1028,9 +1032,11 @@ export class QueryBuilder {
1028
1032
  * @internal
1029
1033
  */
1030
1034
  addPolymorphicJoin(prop, targetMeta, ownerAlias, alias, type, path, schema) {
1031
- // Override referencedColumnNames to use the specific target's PK columns
1035
+ // Override referencedColumnNames to use the specific target's PK (or `targetKey`) columns
1032
1036
  // (polymorphic targets may have different PK column names, e.g. org_id vs user_id)
1033
- const referencedColumnNames = targetMeta.getPrimaryProps().flatMap(pk => pk.fieldNames);
1037
+ const referencedColumnNames = prop.targetKey
1038
+ ? targetMeta.properties[prop.targetKey].fieldNames
1039
+ : targetMeta.getPrimaryProps().flatMap(pk => pk.fieldNames);
1034
1040
  const targetProp = { ...prop, targetMeta, referencedColumnNames };
1035
1041
  const aliasedName = `${ownerAlias}.${prop.name}[${targetMeta.className}]#${alias}`;
1036
1042
  this.#state.joins[aliasedName] = this.helper.joinManyToOneReference(targetProp, ownerAlias, alias, type, {}, schema);
@@ -1566,7 +1572,7 @@ export class QueryBuilder {
1566
1572
  return;
1567
1573
  }
1568
1574
  if (prop?.embedded || (prop?.kind === ReferenceKind.EMBEDDED && prop.object)) {
1569
- const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
1575
+ const name = prop.formula ? prop.name : (prop.embeddedPath?.join('.') ?? prop.fieldNames[0]);
1570
1576
  const aliased = this.#state.aliases[a] ? `${a}.${name}` : name;
1571
1577
  ret.push(getFieldName(aliased, customAlias));
1572
1578
  return;
@@ -1581,11 +1587,11 @@ export class QueryBuilder {
1581
1587
  }
1582
1588
  const nest = (prop) => {
1583
1589
  for (const childProp of Object.values(prop.embeddedProps)) {
1584
- if (childProp.persist === false) {
1590
+ if (childProp.persist === false && !childProp.formula) {
1585
1591
  continue;
1586
1592
  }
1587
1593
  if (childProp.fieldNames && (childProp.kind !== ReferenceKind.EMBEDDED || childProp.object)) {
1588
- const name = childProp.fieldNames[0];
1594
+ const name = childProp.formula ? childProp.name : childProp.fieldNames[0];
1589
1595
  ret.push(getFieldName(this.#state.aliases[a] ? `${a}.${name}` : name));
1590
1596
  }
1591
1597
  else {
@@ -1666,7 +1672,8 @@ export class QueryBuilder {
1666
1672
  return `${currentAlias}.${embeddedProp.fieldNames[0]}`;
1667
1673
  }
1668
1674
  }
1669
- return `${currentAlias}.${embeddedProp?.fieldNames?.[0] ?? propName}`;
1675
+ const name = embeddedProp?.formula ? embeddedProp.name : (embeddedProp?.fieldNames?.[0] ?? propName);
1676
+ return `${currentAlias}.${name}`;
1670
1677
  }
1671
1678
  // Handle relations - auto-join if not the last part
1672
1679
  if (prop.kind === ReferenceKind.MANY_TO_ONE ||
@@ -1759,6 +1766,9 @@ export class QueryBuilder {
1759
1766
  case QueryType.COUNT: {
1760
1767
  const fields = this.#state.fields.map(f => this.helper.mapper(f, this.type, undefined, undefined, schema));
1761
1768
  qb.count(fields, this.#state.flags.has(QueryFlag.DISTINCT));
1769
+ if (this.#state.distinctOn && this.#state.flags.has(QueryFlag.DISTINCT)) {
1770
+ qb.distinctOn(this.prepareFields(this.#state.distinctOn, 'where', schema));
1771
+ }
1762
1772
  this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
1763
1773
  break;
1764
1774
  }
@@ -2322,7 +2332,10 @@ export class QueryBuilder {
2322
2332
  const fieldName = this.helper.mapper(field, this.type, undefined, null);
2323
2333
  // virtual fields (e.g. `qb.as(...)`) have no column to reference inside `min()`; inline their
2324
2334
  // expression instead, as a select alias is not resolvable there on some dialects (e.g. PostgreSQL)
2325
- const virtual = !prop?.persist && !prop?.formula && !prop?.hasConvertToJSValueSQL && !pks.includes(fieldName)
2335
+ const virtual = (!prop || prop.persist === false) &&
2336
+ !prop?.formula &&
2337
+ !prop?.hasConvertToJSValueSQL &&
2338
+ !pks.includes(fieldName)
2326
2339
  ? this.resolveVirtualField(f, fieldName)
2327
2340
  : undefined;
2328
2341
  const expr = virtual ? virtual.expr : this.platform.quoteIdentifier(fieldName);
@@ -112,7 +112,7 @@ export class QueryBuilderHelper {
112
112
  const aliasPrefix = isTableNameAliasRequired ? resolvedAlias + '.' : '';
113
113
  const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
114
114
  const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
115
- if (a === prop?.embedded?.[0]) {
115
+ if (a === prop?.embedded?.[0] && !prop.formula) {
116
116
  return aliasPrefix + prop.fieldNames[fkIdx];
117
117
  }
118
118
  const noPrefix = prop?.persist === false;
@@ -120,12 +120,13 @@ export class QueryBuilderHelper {
120
120
  return raw(this.prefix(field, isTableNameAliasRequired));
121
121
  }
122
122
  if (prop?.formula) {
123
- const alias2 = this.#platform.quoteIdentifier(a).toString();
123
+ const formulaAlias = isTableAlias ? a : this.#alias;
124
+ const alias2 = this.#platform.quoteIdentifier(formulaAlias).toString();
124
125
  const aliasName = alias === undefined ? prop.fieldNames[0] : alias;
125
126
  const as = aliasName === null ? '' : ` as ${this.#platform.quoteIdentifier(aliasName)}`;
126
127
  const meta = this.#aliasMap[a]?.meta ?? this.#metadata.get(this.#entityName);
127
128
  const table = this.createFormulaTable(alias2, meta, schema);
128
- const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, a), alias2);
129
+ const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, formulaAlias), alias2);
129
130
  let value = this.#driver.evaluateFormula(prop.formula, columns, table);
130
131
  if (!this.isTableNameAliasRequired(type)) {
131
132
  value = value.replaceAll(alias2 + '.', '');