Skip to content

Commit 53814be

Browse files
committed
feat(data-explorer): COMPASS-9742-add-add-field
1 parent 993bdcc commit 53814be

File tree

4 files changed

+102
-7
lines changed

4 files changed

+102
-7
lines changed

packages/compass-data-modeling/src/components/diagram-editor.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { DataModelingState } from '../store/reducer';
1010
import {
1111
addNewFieldToCollection,
1212
moveCollection,
13+
onAddNestedField,
1314
selectCollection,
1415
selectRelationship,
1516
selectBackground,
@@ -131,6 +132,7 @@ const DiagramContent: React.FunctionComponent<{
131132
isInRelationshipDrawingMode: boolean;
132133
editErrors?: string[];
133134
newCollection?: string;
135+
onAddFieldToObjectField: (ns: string, parentPath: string[]) => void;
134136
onAddNewFieldToCollection: (ns: string) => void;
135137
onMoveCollection: (ns: string, newPosition: [number, number]) => void;
136138
onCollectionSelect: (namespace: string) => void;
@@ -153,6 +155,7 @@ const DiagramContent: React.FunctionComponent<{
153155
model,
154156
isInRelationshipDrawingMode,
155157
newCollection,
158+
onAddFieldToObjectField,
156159
onAddNewFieldToCollection,
157160
onMoveCollection,
158161
onCollectionSelect,
@@ -324,6 +327,13 @@ const DiagramContent: React.FunctionComponent<{
324327
[onAddNewFieldToCollection]
325328
);
326329

330+
const onClickAddFieldToObjectField = useCallback(
331+
(event: React.MouseEvent, nodeId: string, parentPath: string[]) => {
332+
onAddFieldToObjectField(nodeId, parentPath);
333+
},
334+
[onAddFieldToObjectField]
335+
);
336+
327337
return (
328338
<div
329339
ref={setDiagramContainerRef}
@@ -361,6 +371,7 @@ const DiagramContent: React.FunctionComponent<{
361371
onNodeDragStop={onNodeDragStop}
362372
onConnect={onConnect}
363373
onAddFieldToNodeClick={onClickAddFieldToCollection}
374+
onAddFieldToObjectFieldClick={onClickAddFieldToObjectField}
364375
/>
365376
</div>
366377
</div>
@@ -384,6 +395,7 @@ const ConnectedDiagramContent = connect(
384395
},
385396
{
386397
onAddNewFieldToCollection: addNewFieldToCollection,
398+
onAddFieldToObjectField: onAddNestedField,
387399
onMoveCollection: moveCollection,
388400
onCollectionSelect: selectCollection,
389401
onRelationshipSelect: selectRelationship,

packages/compass-data-modeling/src/store/diagram.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,38 @@ export function redoEdit(): DataModelingThunkAction<void, RedoEditAction> {
500500
};
501501
}
502502

503+
export function onAddNestedField(
504+
ns: string,
505+
parentFieldPath: string[]
506+
): DataModelingThunkAction<void, ApplyEditAction | ApplyEditFailedAction> {
507+
return (dispatch, getState) => {
508+
const modelState = selectCurrentModelFromState(getState());
509+
510+
const collection = modelState.collections.find((c) => c.ns === ns);
511+
if (!collection) {
512+
throw new Error('Collection to add field to not found');
513+
}
514+
515+
const edit: Omit<
516+
Extract<Edit, { type: 'AddField' }>,
517+
'id' | 'timestamp'
518+
> = {
519+
type: 'AddField',
520+
ns,
521+
// Use the first unique field name we can use.
522+
field: [
523+
...parentFieldPath,
524+
getNewUnusedFieldName(collection.jsonSchema, parentFieldPath),
525+
],
526+
jsonSchema: {
527+
bsonType: 'string',
528+
},
529+
};
530+
531+
return dispatch(applyEdit(edit));
532+
};
533+
}
534+
503535
export function addNewFieldToCollection(
504536
ns: string
505537
): DataModelingThunkAction<void, ApplyEditAction | ApplyEditFailedAction> {

packages/compass-data-modeling/src/utils/nodes-and-edges.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import toNS from 'mongodb-ns';
2-
import type { NodeProps, EdgeProps, NodeGlyph } from '@mongodb-js/diagramming';
2+
import type {
3+
NodeProps,
4+
EdgeProps,
5+
NodeGlyph,
6+
NodeField,
7+
} from '@mongodb-js/diagramming';
38
import type { MongoDBJSONSchema } from 'mongodb-schema';
49
import type { SelectedItems } from '../store/diagram';
510
import type {
@@ -33,6 +38,37 @@ export const getHighlightedFields = (
3338
return selection;
3439
};
3540

41+
const getBaseNodeField = (fieldPath: string[]): NodeField => {
42+
return {
43+
name: fieldPath[fieldPath.length - 1],
44+
id: fieldPath,
45+
depth: fieldPath.length - 1,
46+
};
47+
};
48+
49+
/**
50+
* Create the base field list to be used for positioning and measuring in node layouts.
51+
*/
52+
export const getBaseFieldsFromSchema = ({
53+
jsonSchema,
54+
}: {
55+
jsonSchema: MongoDBJSONSchema;
56+
}): NodeField[] => {
57+
if (!jsonSchema || !jsonSchema.properties) {
58+
return [];
59+
}
60+
const fields: NodeField[] = [];
61+
62+
traverseSchema({
63+
jsonSchema,
64+
visitor: ({ fieldPath }) => {
65+
fields.push(getBaseNodeField(fieldPath));
66+
},
67+
});
68+
69+
return fields;
70+
};
71+
3672
const KEY_GLYPH: NodeGlyph[] = ['key'];
3773
const NO_GLYPH: NodeGlyph[] = [];
3874

@@ -54,17 +90,18 @@ export const getFieldsFromSchema = ({
5490
jsonSchema,
5591
visitor: ({ fieldPath, fieldTypes }) => {
5692
fields.push({
57-
name: fieldPath[fieldPath.length - 1],
58-
id: fieldPath,
93+
...getBaseNodeField(fieldPath),
5994
type: fieldTypes.length === 1 ? fieldTypes[0] : fieldTypes,
95+
id: fieldPath,
6096
depth: fieldPath.length - 1,
6197
glyphs:
6298
fieldTypes.length === 1 && fieldTypes[0] === 'objectId'
6399
? KEY_GLYPH
64100
: NO_GLYPH,
65101
selectable: true,
66102
selected:
67-
!!selectedField && areFieldPathsEqual(fieldPath, selectedField),
103+
!!selectedField?.length &&
104+
areFieldPathsEqual(fieldPath, selectedField),
68105
variant:
69106
highlightedFields.length &&
70107
highlightedFields.some((highlightedField) =>
@@ -96,7 +133,7 @@ export function collectionToBaseNodeForLayout({
96133
x: displayPosition[0],
97134
y: displayPosition[1],
98135
},
99-
fields: getFieldsFromSchema({ jsonSchema }),
136+
fields: getBaseFieldsFromSchema({ jsonSchema }),
100137
};
101138
}
102139

packages/compass-data-modeling/src/utils/schema.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import type { MongoDBJSONSchema } from 'mongodb-schema';
22

3-
export function getNewUnusedFieldName(jsonSchema: MongoDBJSONSchema): string {
4-
const existingFieldNames = new Set(Object.keys(jsonSchema.properties || {}));
3+
export function getNewUnusedFieldName(
4+
jsonSchema: MongoDBJSONSchema,
5+
parentFieldPath: string[] = []
6+
): string {
7+
let parentJSONSchema: MongoDBJSONSchema | undefined = jsonSchema;
8+
for (const currentField of parentFieldPath) {
9+
if (!currentField) {
10+
throw new Error('Invalid field path to get new field name');
11+
}
12+
parentJSONSchema = parentJSONSchema?.properties?.[currentField];
13+
}
14+
15+
const existingFieldNames = new Set(
16+
Object.keys(parentJSONSchema?.properties || {})
17+
);
18+
519
let i = 1;
620
let fieldName = `field-${i}`;
721

0 commit comments

Comments
 (0)