diff --git a/_assets/css/bundle.scss b/_assets/css/bundle.scss
index 97855e136e..f5c0db48fb 100644
--- a/_assets/css/bundle.scss
+++ b/_assets/css/bundle.scss
@@ -363,6 +363,10 @@ h2 {
margin-top: 40px;
}
+h3 {
+ font-size: 150%;
+}
+
.smaller {
font-size: 80%;
}
@@ -404,6 +408,11 @@ h2 {
vertical-align: middle;
}
+.bg-gray {
+ background-color: #ccc;
+ color: black;
+}
+
.inline-button {
background-color: #496276;
color: white;
@@ -552,6 +561,12 @@ h2 {
font-size: 85%;
line-height: 0.85;
}
+.tut-nav {
+ padding: 0;
+ margin: 0;
+ font-size: 85%;
+ line-height: 0.85;
+}
.list-group-item.active, .list-group-item.active:hover,
.list-group-item.active:focus {
@@ -635,6 +650,9 @@ details {
> img {
padding-bottom: 10px;
}
+ > pre {
+ padding-bottom: 10px;
+ }
}
details > summary:before {
content: "► ";
diff --git a/_data/sidenav.yml b/_data/sidenav.yml
index ee3160252d..2ea8e816f2 100644
--- a/_data/sidenav.yml
+++ b/_data/sidenav.yml
@@ -157,7 +157,7 @@ main:
{ url: '/refs/gradle-tasks/', text: 'DHF Gradle Tasks' },
{ url: '/refs/server-side-library/', text: 'Server Side API Library' },
{ url: '/refs/security/', text: 'Security' },
- # { url: '/refs/index-settings/', text: 'Index Settings' },
+ { url: '/refs/index-settings/', text: 'Index Settings' },
]
}
- { url: '/refs/faqs/', text: 'FAQs' }
diff --git a/_data/tut4xentprops.yml b/_data/tut4xentprops.yml
new file mode 100644
index 0000000000..d0d28d1507
--- /dev/null
+++ b/_data/tut4xentprops.yml
@@ -0,0 +1,46 @@
+'Product': [
+ {
+ name: 'sku',
+ type: 'string',
+ indexes: [ 'key' ],
+ },
+ {
+ name: 'price',
+ type: 'decimal',
+ },
+]
+
+'Order': [
+ {
+ name: 'id',
+ type: 'string',
+ indexes: [ 'key', 'element range' ],
+ },
+ {
+ name: 'total',
+ type: 'decimal',
+ },
+ {
+ name: 'products',
+ entitytype: 'Product',
+ cardinality: '1..∞',
+ },
+]
+
+'Customer': [
+ {
+ name: 'id',
+ type: 'string',
+ indexes: [ 'key' ],
+ },
+ {
+ name: 'billing_address',
+ type: 'string',
+ indexes: [ 'pii' ],
+ },
+ {
+ name: 'shipping_address',
+ type: 'string',
+ indexes: [ 'pii' ],
+ },
+]
diff --git a/_data/tut4xtoc.yml b/_data/tut4xtoc.yml
index 9a4a6b4a0b..bb41e4335b 100644
--- a/_data/tut4xtoc.yml
+++ b/_data/tut4xtoc.yml
@@ -3,15 +3,14 @@
text: 'Install the Data Hub Framework',
}
- {
- text: 'Loading the Raw Data',
+ text: 'Load the Raw Data',
childPages: [
{ url: '/tutorial/4x/create-entities/', text: 'Create the Entities' },
- { url: '/tutorial/4x/create-input-flows/', text: 'Create the Input Flows' },
- { url: '/tutorial/4x/config-run-input-flows/', text: 'Configure and Run the Input Flows' },
+ { url: '/tutorial/4x/create-run-input-flows/', text: 'Create and Run the Input Flows' },
]
}
- {
- text: 'Viewing Jobs, Traces, and Data',
+ text: 'View Jobs, Traces, and the Data',
childPages: [
{ url: '/tutorial/4x/view-jobs/', text: 'View the Jobs Log' },
{ url: '/tutorial/4x/view-traces/', text: 'View the Traces Log' },
@@ -19,26 +18,18 @@
]
}
- {
- text: 'Harmonizing Data',
+ text: 'Harmonize the Data',
childPages: [
- { url: '/tutorial/4x/modeling-product-entity/', text: 'Model the Product Entity' },
- { url: '/tutorial/4x/mapping-product-entity/', text: 'Create a Product Source-to-Entity Mapping' },
- { url: '/tutorial/4x/harmonizing-product-data/', text: 'Harmonize the Product Data' },
+ { url: '/tutorial/4x/harmonize-product-data-by-mappings/', text: 'Harmonize the "Product" Data by Mappings' },
+ { url: '/tutorial/4x/harmonize-order-data-by-custom-code/', text: 'Harmonize the "Order" Data by Custom Code' },
+ { url: '/tutorial/4x/secure-pii-in-customer-data/', text: 'Secure PII in the "Customer" Data' },
]
}
- {
- text: 'Harmonizing Orders',
- childPages: [
- { url: '/tutorial/4x/modeling-order-entity/', text: 'Model the Order Entity' },
- { url: '/tutorial/4x/harmonizing-order-data/', text: 'Harmonize the Order Data' },
- { url: '/tutorial/4x/securing-pii/', text: 'Securing PII' },
- ]
- }
-- {
- url: '/tutorial/4x/serve-data/',
- text: 'Serve the Data Out of MarkLogic',
+ url: '/tutorial/4x/access-data/',
+ text: 'Access the Data from MarkLogic Server',
}
- {
- url: '/tutorial/4x/wrapping-up/',
- text: 'Wrapping Up',
+ url: '/tutorial/4x/takeaways/',
+ text: 'Takeaways',
}
diff --git a/_includes/conrefs/conref-qs-4x-browse-data.md b/_includes/conrefs/conref-qs-4x-browse-data.md
index 748cd489fa..ae760a4338 100644
--- a/_includes/conrefs/conref-qs-4x-browse-data.md
+++ b/_includes/conrefs/conref-qs-4x-browse-data.md
@@ -1,16 +1,17 @@
-{% assign pickdb="the STAGING database or the FINAL database" %}
-{% if include.pickdb %}{% assign pickdb=include.pickdb %}{% endif %}
+{% assign pickdbphrase="the STAGING database or the FINAL database" %}
+{% if include.pickdb %}{% assign pickdbphrase="the **" | append: include.pickdb | append: "** database" %}{% endif %}
{% assign pickitem="the dataset item you want to view" %}
{% if include.pickitem %}{% assign pickitem=include.pickitem %}{% endif %}
-
-{% assign full-imgpath=include.imgpath | append: "qs-4x-browse-data-full.png" %}
-{% include thumbnail.html imgfile=full-imgpath alttext="Browse Data view" imgclass="screenshot" tab=" " %}
+{% assign pref="qs-4x-browse-data" %}
+{% if include.pickdb %}{% assign suf="-" | append: include.pickdb %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Browse Data" imgclass="screenshot" tab=" " %}
1. In the QuickStart menu, click **Browse Data**{:.uimenuitem}.
-1. From the database selection dropdown, choose {{ pickdb }}.
-1. To narrow the list to include entities only, check the **Entities Only**{:.uilabel} box.
+1. From the database selection dropdown, choose {{ pickdbphrase }}.
+1. (Optional) To narrow the list to include entities only, check the **Entities Only**{:.uilabel} box.
{% include note-in-list.html type="TIP" content="You can further filter the list by using the free-text search field or the faceted search filters." %}
1. In the list, click the row of {{ pickitem }}.
{:.ol-steps}
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-create-entity.md b/_includes/conrefs/conref-qs-4x-create-entity.md
index cd02a3e4a7..fffef46fc9 100644
--- a/_includes/conrefs/conref-qs-4x-create-entity.md
+++ b/_includes/conrefs/conref-qs-4x-create-entity.md
@@ -2,7 +2,7 @@
To create an entity named `{{ include.entityname }}`,
{% assign pref="qs-4x-entities-create-complete" %}
-{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% endif %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
{% include thumbnail.html imgfile=full-imgpath alttext="New Entities form" imgclass="screenshot" tab=" " %}
diff --git a/_includes/conrefs/conref-qs-4x-create-input-flow.md b/_includes/conrefs/conref-qs-4x-create-input-flow.md
deleted file mode 100644
index 2572a7e038..0000000000
--- a/_includes/conrefs/conref-qs-4x-create-input-flow.md
+++ /dev/null
@@ -1,14 +0,0 @@
-{% if include.fullsteps %}
-To create an input flow for the `{{ include.entityname }}` entity,
-
-{% assign pref="qs-4x-flows-create-complete" %}
-{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% endif %}
-{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
-{% include thumbnail.html imgfile=full-imgpath alttext="Create Input Flow form" imgclass="screenshot" tab=" " %}
-
-1. In QuickStart's navigation bar, click **Flows**{:.uimenuitem}.{% endif %}
-1. {% assign full-text = "Expand **" | append: include.entityname | append: "**{:.uilabel} in the left panel." %}{{ full-text }}
-1. Click the **+**{:.uilabel} for **Input Flows**{:.uilabel}.
-1. {% assign full-text = "In the **Create Input Flow**{:.uilabel} dialog, set **Input Flow Name**{:.uilabel} to " | append: include.inputflowname | append: "
." %}{{ full-text }}
-1. Click **CREATE**{:.inline-button}.
-{:.ol-steps}
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-create-run-harmonize-flow.md b/_includes/conrefs/conref-qs-4x-create-run-harmonize-flow.md
new file mode 100644
index 0000000000..91b956c593
--- /dev/null
+++ b/_includes/conrefs/conref-qs-4x-create-run-harmonize-flow.md
@@ -0,0 +1,32 @@
+{% if include.create %}
+{% if include.fullsteps %}
+To create a harmonization flow for the `{{ include.entityname }}` entity,
+
+{% assign pref="qs-4x-flows-create-harmonize-flow" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Create Harmonize Flow form" imgclass="screenshot" tab=" " %}
+
+1. In QuickStart's navigation bar, click **Flows**{:.uimenuitem}.{% endif %}
+1. {% assign full-text = "Expand **" | append: include.entityname | append: "**{:.uilabel} in the left panel." %}{{ full-text }}
+1. Click the **+**{:.uilabel} for **Harmonize Flows**{:.uilabel}.
+1. {% assign full-text = "In the **Create Harmonize Flow**{:.uilabel} dialog, set **Harmonize Flow Name**{:.uilabel} to " | append: include.harmonizeflowname | append: "
." %}{{ full-text }}
+{% if include.mappingname %}1. Under **Mapping Generation**{:.uilabel}, check **{{ include.mappingname }}** .{% endif %}
+1. Click **CREATE**{:.inline-button}.
+{:.ol-steps}
+{% endif %}
+
+{% if include.run %}
+{% if include.fullsteps %} When you create a flow with mapping, QuickStart automatically generates harmonization code based on the entity model and the mapping and then deploys the code to MarkLogic Server.
+
+To run the harmonization flow,{% endif %}
+
+{% assign pref="qs-4x-flows-run-harmonize-flow" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Run Flow form" imgclass="screenshot" tab=" " %}
+
+1. Click the **Flow Info**{:.uilabel} tab.
+1. Click **Run Harmonize**{:.inline-button}.
+{:.ol-steps}
+{% endif %}
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-config-run-input-flows.md b/_includes/conrefs/conref-qs-4x-create-run-input-flow.md
similarity index 68%
rename from _includes/conrefs/conref-qs-4x-config-run-input-flows.md
rename to _includes/conrefs/conref-qs-4x-create-run-input-flow.md
index 2f6bfa3b94..4c46d6f10a 100644
--- a/_includes/conrefs/conref-qs-4x-config-run-input-flows.md
+++ b/_includes/conrefs/conref-qs-4x-create-run-input-flow.md
@@ -1,17 +1,33 @@
-{% if include.fullsteps %}
-To configure and run the `{{ include.jobname }}` input flow,
+### 1 - Create an input flow for the **{{ include.entityname }}** entity.
-{% assign pref="qs-4x-flows-load-data" %}
-{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% endif %}
+{% if include.fullsteps %}
+{% assign pref="qs-4x-flows-create-input-flow" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
-{% include thumbnail.html imgfile=full-imgpath alttext="" imgclass="screenshot" tab=" " %}
-{% endif %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Create Input Flow form" imgclass="screenshot" tab=" " %}
+1. In QuickStart's navigation bar, click **Flows**{:.uimenuitem}.{% endif %}
1. {% assign full-text = "Expand **" | append: include.entityname | append: "**{:.uilabel} in the left panel." %}{{ full-text }}
-1. Click **{{ include.jobname }}**{:.uilabel} under **Input Flows**{:.uilabel}.
+1. Click the **+**{:.uilabel} for **Input Flows**{:.uilabel}.
+1. {% assign full-text = "In the **Create Input Flow**{:.uilabel} dialog, set **Input Flow Name**{:.uilabel} to " | append: include.inputflowname | append: "
." %}{{ full-text }}
+1. Click **CREATE**{:.inline-button}.
+{:.ol-steps}
+
+{% if include.fullsteps %}
+**Result**
+
+ - Your new flow appears under **Input Flows**{:.uilabel} in the left panel.
+ - The **Run Input Flow**{:.uilabel} wizard appears on the right.
+{% endif %}
+
+
+### 2 - Configure and run the **{{ include.inputflowname }}** flow.
{% if include.fullsteps %}
- **Result:** The **Run Input Flow**{:.uilabel} wizard appears.
+{% assign pref="qs-4x-flows-load-data" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="" imgclass="screenshot" tab=" " %}
{% endif %}
{% assign full-imgpath=include.imgpath | append: "qs-4x-flows-load-raw-input-files" | append: suf | append: ".png" %}
diff --git a/_includes/conrefs/conref-qs-4x-define-entity-model-add-props.md b/_includes/conrefs/conref-qs-4x-define-entity-model-add-props.md
new file mode 100644
index 0000000000..4e75d87dc0
--- /dev/null
+++ b/_includes/conrefs/conref-qs-4x-define-entity-model-add-props.md
@@ -0,0 +1,32 @@
+
{% if include.counter == 0 %}In the {{ include.entityname }} entity editor, click + in the Properties section to add a new property.
+{% else %}Click + again to add another property.{% endif %}
+
+
+
+ - Set Name to
{{ include.propname }}
.
+
+ {% if include.propentitytype %}
+ -
+ {% assign pref="qs-4x-entities-add-properties-type-entities" %}
+ {% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+ {% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+ {% assign full-text="Set Type to the entity " | append: include.propentitytype | append: "." %}
+ {% include step-collapsed.html steptext=full-text stepimg=full-imgpath imgclass="screenshot" nonum=true %}
+
+ {% else %}
+ - Set Type to
{{ include.proptype }}
.
+ {% endif %}
+
+ {% if include.propindexes contains "key" %}- To make
{{ include.propname }}
the primary key, click the area in the key column for the {{ include.propname }}
row. {% endif %}
+
+ {% if include.propindexes contains "element range" %}- To specify that
{{ include.propname }}
needs an element range index, click the area in the lightning bolt column for the {{ include.propname }}
row. {% endif %}
+
+ {% if include.propindexes contains "pii" %}- To mark
{{ include.propname }}
as PII, click the area in the key column for the {{ include.propname }}
row. {% endif %}
+
+ {% if include.propcardinality %}
+ - To indicate that a(n) {{ include.entityname }} instance can have multiple instances of this property, set Cardinality to 1..∞.
+ {% endif %}
+
+
+
+
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-define-entity-model.md b/_includes/conrefs/conref-qs-4x-define-entity-model.md
new file mode 100644
index 0000000000..dfc426f93e
--- /dev/null
+++ b/_includes/conrefs/conref-qs-4x-define-entity-model.md
@@ -0,0 +1,45 @@
+{% if include.fullsteps %}
+To define the `{{ include.entityname }}` entity model,
+
+{% assign pref="qs-4x-entities-add-properties" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="entity properties" imgclass="screenshot" tab=" " %}
+{% endif %}
+
+
+
+ {% if include.fullsteps %}- In QuickStart's navigation bar, click .
{% endif %}
+
+ - At the top of the {{ include.entityname }} entity card, click the pencil icon to edit the {{ include.entityname }} entity definition.
+
+ {% assign counter = 0 %}
+ {% for prop in site.data.tut4xentprops.[include.entityname] %}
+ {% include conrefs/conref-qs-4x-define-entity-model-add-props.md
+ entityname=include.entityname
+ propname=prop.name
+ proptype=prop.type
+ propentitytype=prop.entitytype
+ propcardinality=prop.cardinality
+ propindexes=prop.indexes
+ imgpath=include.imgpath
+ counter=counter
+ %}
+ {% assign counter = counter | plus: 1 %}
+ {% endfor %}
+
+ - Click SAVE.
+
+ -
+ {% assign full-imgpath=include.imgpath | append: "qs-4x-update-indexes-yes.png" %}
+ {% assign full-text="If prompted to update the index, click Yes." %}
+ {% include step-collapsed.html steptext=full-text stepimg=full-imgpath imgclass="img-small" nonum=true %}
+
+
+ -
+ {% assign full-imgpath=include.imgpath | append: "qs-4x-entities-resize-card.png" %}
+ {% assign full-text="Drag the bottom-right corner of the entity card to resize it and see the newly added properties." %}
+ {% include step-collapsed.html steptext=full-text stepimg=full-imgpath imgclass="img-small" nonum=true %}
+
+
+
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-define-source-to-entity-maps.md b/_includes/conrefs/conref-qs-4x-define-source-to-entity-maps.md
new file mode 100644
index 0000000000..85f5821ebc
--- /dev/null
+++ b/_includes/conrefs/conref-qs-4x-define-source-to-entity-maps.md
@@ -0,0 +1,58 @@
+{% if include.fullsteps %}
+To create an mapping named `{{ include.mappingname }}`,
+
+{% assign pref="qs-4x-mappings-create-complete" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext=include.mappingname imgclass="screenshot" tab=" " %}
+
+1. In QuickStart's navigation bar, click **Mapping**{:.uimenuitem}.{% endif %}
+1. In the left panel, click the **+**{:.uilabel} icon for the **{{ include.entityname }}**{:.uilabel} entity.
+1. {% assign full-text = "In the **Create New Mapping**{:.uilabel} form, set **Mapping Name**{:.uilabel} to " | append: include.mappingname | append: "
." %}{{ full-text }}
+1. Click **CREATE**{:.inline-button}.
+{:.ol-steps}
+{% if include.fullsteps %}
+
+Your new mapping (**{{ include.mappingname }}**{:.uilabel}) appears under **{{ include.entityname }}**{:.uilabel} in the left panel.
+
+The mapping editor displays a row for each property in your entity model. In each row,
+- the right column displays the entity property, and
+- the left column contains a dropdown list from which you can select the source field that corresponds to that entity property.
+{% endif %}
+
+{% if include.fullsteps %}
+To configure the mapping,
+
+{% assign pref="qs-4x-mappings-editor" %}
+{% if include.entityname %}{% assign suf="-" | append: include.entityname %}{% else %}{% assign suf="" %}{% endif %}
+{% assign full-imgpath=include.imgpath | append: pref | append: suf | append: ".png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Mapping editor" imgclass="screenshot" tab=" " %}{% endif %}
+
+1. For each entity property, expand the dropdown list under **Source**{:.uilabel} and select the source field that corresponds to that entity property.
+
+ {% include note-in-list.html type="TIP" content="You can enter part of the field name to filter the dropdown list." %}
+
+1. Click **SAVE MAPPING**{:.inline-button}.
+{:.ol-steps}
+
+
+{% if include.diffsource %}
+QuickStart selects one of the items ingested into the STAGING database.
+ - The URI of that selected item is displayed at the top of the **Source**{:.uilabel} column of the mapping editor.
+ - The fields of that item are presented in the dropdown lists.
+
+To choose a different source item to map to your entity model,
+
+1. Get the URI of a different source item.
+ 1. In QuickStart's navigation bar, click **Browse Data**{:.uimenuitem}.
+ 1. Select the `STAGING` database if not already selected.
+ 1. Narrow the list by clicking on the **{{ include.entityname }}**{:.uilabel} collection among the filters on the left.
+ 1. Select an item and click its copy icon ****{:.inline-button} to copy its URI to your clipboard.
+ {:.ol-substeps}
+1. Replace the selected item in the mapping editor.
+ 1. In QuickStart's navigation bar, click **Mapping**{:.uimenuitem}.
+ 1. In the left panel, click the mapping you just created (**{{ include.entityname }} Mapping**{:.uilabel}).
+ 1. In the mapping editor under **Source**{:.uilabel}, click the pencil icon ****{:.inline-button} next to the **URI**{:.uilabel}.
+ {:.ol-substeps}
+{:.ol-steps}
+{% endif %}
\ No newline at end of file
diff --git a/_includes/conrefs/conref-qs-4x-identify-pii.md b/_includes/conrefs/conref-qs-4x-identify-pii.md
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/_includes/conrefs/conref-qs-4x-jobs.md b/_includes/conrefs/conref-qs-4x-jobs.md
index dad161877e..bb3fa80941 100644
--- a/_includes/conrefs/conref-qs-4x-jobs.md
+++ b/_includes/conrefs/conref-qs-4x-jobs.md
@@ -1,10 +1,11 @@
+{% if include.fullsteps %}
{% assign pickitem="the job you want to view" %}
{% if include.pickitem %}{% assign pickitem=include.pickitem %}{% endif %}
To view the results of {{ pickitem }},
{% assign full-imgpath=include.imgpath | append: "qs-4x-jobs-full.png" %}
-{% include thumbnail.html imgfile=full-imgpath alttext="Jobs log" imgclass="screenshot" tab=" " %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Jobs log" imgclass="screenshot" tab=" " %}{% endif %}
1. In the QuickStart menu, click **Jobs**{:.uimenuitem} to open the Jobs list.
1. In the list, click **>_**{:.inline-button} for {{ pickitem }}.
diff --git a/_includes/conrefs/conref-qs-4x-traces.md b/_includes/conrefs/conref-qs-4x-traces.md
index 481f77e15a..3a894e2cfb 100644
--- a/_includes/conrefs/conref-qs-4x-traces.md
+++ b/_includes/conrefs/conref-qs-4x-traces.md
@@ -15,6 +15,8 @@ To view {{ pickitem }},
{% include note-in-list.html type="TIP" content="You can filter the list by using the free-text search field or the faceted search filters." %}
{:.ol-steps}
+**Results:** The trace details page is displayed for the document you selected.
+
{% assign full-imgpath=include.imgpath | append: "qs-4x-traces-content.png" %}
{% include thumbnail.html imgfile=full-imgpath alttext="Trace detail for the Content plugin" imgclass="screenshot" tab=" " %}
diff --git a/_includes/step-collapsed.html b/_includes/step-collapsed.html
index a69fefc0a9..23478016e7 100644
--- a/_includes/step-collapsed.html
+++ b/_includes/step-collapsed.html
@@ -1,7 +1,7 @@
-{% assign myclass="screenshot" %}
+{% assign myclass="screenshot"- %}
{% if include.imgclass %}{% assign myclass=include.imgclass %}{% endif %}
-1. {{ include.steptext }}
-
+{% if include.nonum %}{% else %}1. {% endif %}{{ include.steptext }}
+ {% if include.preimgtext %}{{ include.preimgtext }}
{% endif %}
-
+ {% if include.postimgtext %}{{ include.postimgtext }}
{% endif %}
\ No newline at end of file
diff --git a/_pages/tutorial/4x/4x.md b/_pages/tutorial/4x/4x.md
index 9e56573fae..91df8d4881 100644
--- a/_pages/tutorial/4x/4x.md
+++ b/_pages/tutorial/4x/4x.md
@@ -23,17 +23,20 @@ This tutorial uses QuickStart, an easy-to-use development tool that you can run
You will perform the following in QuickStart:
1. Load each raw dataset.
-1. Harmonize each dataset.
-1. Secure personally identifiable information in the `Customer` data.
+1. Harmonize each dataset in different ways:
+ - **Product** using mappings
+ - **Order** using code
+ - **Customer** with secured personally identifiable information
1. Serve the data to downstream clients.
+
- Will this tutorial overwrite an existing data hub?
- No, this tutorial creates separate databases and application servers. However, if the default DHF ports (8010, 8011, 8012, 8013) are already in use, you will be warned about the conflicts and then prompted to change them. Other settings will be preserved.
-- How do I delete the data hub created by this tutorial?
-- In QuickStart, select `Settings`. In the next page, select `Uninstall Hub`.
+- Can I delete the data hub created by this tutorial?
+- Yes. See the **Clean Up** section in [Takeaways]({{site.baseurl}}/tutorial/4x/takeaways/).
@@ -46,10 +49,9 @@ You will perform the following in QuickStart:
Check the version of the locally installed MarkLogic Server.
- 1. In a web browser, navigate to [`http://localhost:8001`](http://localhost:8001){:target="_blank"}
- 1. Log in, as needed.
- 1. Check the version information at the top-left corner of the page.
- 
+ 1. In a web browser, navigate to the MarkLogic Server UI ([`http://localhost:8001`](http://localhost:8001){:target="_blank"})
+ 1. Log in if required.
+ 1. The version information is displayed at the top-left corner of the page.
@@ -62,8 +64,6 @@ You will perform the following in QuickStart:
## See Also
-- [Download the completed version of this tutorial.](https://github.com/marklogic/marklogic-data-hub/tree/develop/examples/online-store){:target="_blank"}
- [Tutorial for DHF 3.x]({{site.baseurl}}/tutorial/3x/)
- [Tutorial for DHF 2.x]({{site.baseurl}}/tutorial/2x/)
- [Tutorial for DHF 1.x]({{site.baseurl}}/tutorial/1x/)
-
diff --git a/_pages/tutorial/4x/access-data.md b/_pages/tutorial/4x/access-data.md
new file mode 100644
index 0000000000..4e2dca22ab
--- /dev/null
+++ b/_pages/tutorial/4x/access-data.md
@@ -0,0 +1,28 @@
+---
+layout: inner
+title: Tutorial - Access the Data from MarkLogic Server
+permalink: /tutorial/4x/access-data/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Access the Data from MarkLogic Server
+
+An application can access your harmonized data from MarkLogic Server through several REST endpoints. For a full list of REST endpoints, see [Client API documentation](https://docs.marklogic.com/REST/client){:target="_blank"}.
+
+The following are example searches targeting the default endpoints (port 8010 for the staging database and port 8011 for the final database):
+
+ | database | data | example search links |
+ |---|---|---|
+ | STAGING | raw | [STAGING database search (http://localhost:8010/v1/search?format=json)](http://localhost:8010/v1/search?format=json){:target="_blank"} |
+ | FINAL | harmonized | [FINAL database search (http://localhost:8011/v1/search?format=json)](http://localhost:8011/v1/search?format=json){:target="_blank"} |
+ {:.table-b1gray}
+
+1. Click on the [FINAL database search](http://localhost:8011/v1/search?format=json){:target="_blank"} link.
+
+ An example of results returned:
+ {:.screenshot-border}
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/browse-data.md b/_pages/tutorial/4x/browse-data.md
index b79e3156d5..8da5b3d291 100644
--- a/_pages/tutorial/4x/browse-data.md
+++ b/_pages/tutorial/4x/browse-data.md
@@ -13,7 +13,7 @@ At this point, we have only loaded the raw data; therefore, the staging database
In this exercise, we will browse the ingested data in the staging database.
-{% include conrefs/conref-qs-4x-browse-data.md imgpath=var-imgpath pickdb="the `STAGING` database" pickitem="the first dataset item" %}
+{% include conrefs/conref-qs-4x-browse-data.md imgpath=var-imgpath pickdb="STAGING" pickitem="the first dataset item" %}
### Result
diff --git a/_pages/tutorial/4x/config-run-input-flows.md b/_pages/tutorial/4x/config-run-input-flows.md
deleted file mode 100644
index 6c2cc32641..0000000000
--- a/_pages/tutorial/4x/config-run-input-flows.md
+++ /dev/null
@@ -1,40 +0,0 @@
----
-layout: inner
-title: Tutorial - Configure and Run the Input Flows
-permalink: /tutorial/4x/config-run-input-flows/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Configure and Run the Input Flows
-
-The QuickStart input flow wizard enables you to quickly start loading data without learning the intricacies of the underlying tools. When you run your flow, QuickStart loads data into MarkLogic Server using [MarkLogic Content Pump](https://docs.marklogic.com/guide/mlcp){:target="_blank"} (MLCP), a tool capable of importing a large volume of data into MarkLogic Server.
-
-In this exercise, we configure each input flow to do the following:
- - Load data from the sample data directory.
- - Interpret the input data as delimited text (CSV).
- - Wrap the raw data into envelopes.
- - Automatically generate unique URIs as the data is added to the staging server. This prevents one document from overwriting another if multiple rows contain the same value in the first field.
-
-Then we execute the input flow.
-
-
-## Product
-
-{% include conrefs/conref-qs-4x-config-run-input-flows.md imgpath=var-imgpath entityname="Product" datadir="input\products\games" jobname="Load Products" fullsteps=true %}
-
-
-## Customer
-
-Perform the same steps for **Customer**.
-{% include conrefs/conref-qs-4x-config-run-input-flows.md imgpath=var-imgpath entityname="Customer" datadir="input\customers" jobname="Load Customers" fullsteps=false %}
-
-
-## Order
-
-Perform the same steps for **Order**.
-{% include conrefs/conref-qs-4x-config-run-input-flows.md imgpath=var-imgpath entityname="Order" datadir="input\orders" jobname="Load Orders" fullsteps=false %}
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/create-entities.md b/_pages/tutorial/4x/create-entities.md
index d18853a279..8b17982596 100644
--- a/_pages/tutorial/4x/create-entities.md
+++ b/_pages/tutorial/4x/create-entities.md
@@ -24,18 +24,18 @@ The new `Product` entity card is displayed.
{% assign full-imgpath = var-imgpath | append: "qs-4x-entities-entity-card-Product-00.png" %}{% include thumbnail.html imgfile=full-imgpath alttext="" imgclass="img-results" tab=" " %}
-## Customer
+## Order
-Perform the same steps for **Customer**.
-{% include conrefs/conref-qs-4x-create-entity.md imgpath=var-imgpath entityname="Customer" fullsteps=false %}
+Perform the same steps for **Order**.
+{% include conrefs/conref-qs-4x-create-entity.md imgpath=var-imgpath entityname="Order" fullsteps=false %}
{% include note.html type="TIP" content="Entity cards might be hidden behind the top one. Drag the top entity card to uncover others." %}
-## Order
+## Customer
-Perform the same steps for **Order**.
-{% include conrefs/conref-qs-4x-create-entity.md imgpath=var-imgpath entityname="Order" fullsteps=false %}
+Perform the same steps for **Customer**.
+{% include conrefs/conref-qs-4x-create-entity.md imgpath=var-imgpath entityname="Customer" fullsteps=false %}
{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/create-input-flows.md b/_pages/tutorial/4x/create-input-flows.md
deleted file mode 100644
index 1e9f87e832..0000000000
--- a/_pages/tutorial/4x/create-input-flows.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-layout: inner
-title: Tutorial - Create the Input Flows
-permalink: /tutorial/4x/create-input-flows/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Create the Input Flows
-
-An **input flow** is a series of plugins that ingest data into the staging data hub. Input flows wrap incoming raw data in envelopes and store them in the staging database. The envelopes contain metadata, including those related to lineage and provenance; for example, who loaded the data, when it was loaded, and where it came from.
-
-A **harmonize flow** is another series of plugins that harmonizes the data in the staging database and stores the results in the final database. Harmonization includes standardizing formats, enriching data, resolving duplicates, indexing, and other tasks.
-
-In this section, we create an input flow for each entity: `Product`, `Customer`, and `Order`.
-
-
-## Product
-
-{% include conrefs/conref-qs-4x-create-input-flow.md imgpath=var-imgpath entityname="Product" inputflowname="Load Products" fullsteps=true %}
-
-### Result
-
-Your new flow appears under **Input Flows**{:.uilabel} in the left panel.
- {% assign full-imgpath = var-imgpath | append: "qs-4x-flows-result-load-Product.png" %}{% include thumbnail.html imgfile=full-imgpath alttext="Load Products in the list of input flows" imgclass="img-results" tab=" " %}
-
-
-## Customer
-
-Perform the same steps for **Customer**.
-{% include conrefs/conref-qs-4x-create-input-flow.md imgpath=var-imgpath entityname="Customer" inputflowname="Load Customers" fullsteps=false %}
-
-
-## Order
-
-Perform the same steps for **Order**.
-{% include conrefs/conref-qs-4x-create-input-flow.md imgpath=var-imgpath entityname="Order" inputflowname="Load Orders" fullsteps=false %}
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/create-run-input-flows.md b/_pages/tutorial/4x/create-run-input-flows.md
new file mode 100644
index 0000000000..e61e5f52e3
--- /dev/null
+++ b/_pages/tutorial/4x/create-run-input-flows.md
@@ -0,0 +1,60 @@
+---
+layout: inner
+title: Tutorial - Create and Run the Input Flows
+permalink: /tutorial/4x/create-run-input-flows/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Create the Input Flows
+
+An **input flow** is a series of plugins that ingest data into the staging data hub. Input flows wrap incoming raw data in envelopes and store them in the staging database. The envelopes contain metadata, including those related to lineage and provenance; for example, who loaded the data, when it was loaded, and where it came from.
+
+The QuickStart **Run Input Flow** wizard enables you to quickly start loading data without learning the intricacies of the underlying tools. When you run your flow, QuickStart loads data into MarkLogic Server using [MarkLogic Content Pump](https://docs.marklogic.com/guide/mlcp){:target="_blank"} (MLCP), a tool capable of importing a large volume of data into MarkLogic Server.
+
+In this section, we create and run an input flow for each entity: `Product`, `Customer`, and `Order`. Each input flow performs the following:
+
+ - Load data from the sample data directory.
+ - Interpret the input data as delimited text (CSV), where each row is considered a *document*.
+ - Automatically generate a unique URI to identify the wrapped document as it is added to the staging server. This prevents one document from overwriting another if multiple rows contain the same value in the first field.
+
+
+## Product
+
+{% include conrefs/conref-qs-4x-create-run-input-flow.md
+ imgpath=var-imgpath
+ entityname="Product"
+ inputflowname="Load Products"
+ datadir="input\products\games"
+ jobname="Load Products"
+ fullsteps=true
+%}
+
+
+## Order
+
+Perform the same steps for **Order**.
+{% include conrefs/conref-qs-4x-create-run-input-flow.md
+ imgpath=var-imgpath
+ entityname="Order"
+ inputflowname="Load Orders"
+ datadir="input\orders"
+ jobname="Load Orders"
+ fullsteps=false
+%}
+
+
+## Customer
+
+Perform the same steps for **Customer**.
+{% include conrefs/conref-qs-4x-create-run-input-flow.md
+ imgpath=var-imgpath
+ entityname="Customer"
+ inputflowname="Load Customers"
+ datadir="input\customers"
+ jobname="Load Customers"
+ fullsteps=false %}
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/harmonize-order-data-by-custom-code.md b/_pages/tutorial/4x/harmonize-order-data-by-custom-code.md
new file mode 100644
index 0000000000..10ebba770d
--- /dev/null
+++ b/_pages/tutorial/4x/harmonize-order-data-by-custom-code.md
@@ -0,0 +1,214 @@
+---
+layout: inner
+title: Tutorial - Harmonize the Order Data by Custom Code
+permalink: /tutorial/4x/harmonize-order-data-by-custom-code/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Harmonize the Order Data by Custom Code
+
+Harmonization of the **Order** entity is more complex.
+ - The `price` property of the entity model is the total amount for the entire order; therefore, it must be calculated.
+ - The `product` property is an array of the products ordered, but they are not represented as an array in the source.
+
+Therefore, we must use DHF code scaffolding to generate the harmonization code and then customize it.
+
+
+We have already loaded the **Order** raw data by:
+ - [creating the **Order** entity]({{site.baseurl}}/tutorial/4x/create-entities/) and
+ - [creating and running the associated input flow]({{site.baseurl}}/tutorial/4x/create-run-input-flows/).
+
+In this section, we will:
+ - [Define the entity model](#1_-_define_the_entity_model) by adding properties to the entity model.
+ - [Create the Harmonize flow.](#2_-_create_the_harmonize_flow)
+ - [Customize the Harmonize flow](#3_-_customize_the_harmonize_flow), specifically the **Collector** code and the **Content** code.
+ - Run the Harmonize Flow.
+ - View the results.
+
+
+## 1 - Define the Entity Model
+
+We assume the following about the **Order** data:
+
+ - Each product is identified by its SKU.
+ - Each order can have more than one product.
+ - Each product in the order has a specified quantity.
+ - Each order includes a total amount, which must be calculated.
+
+Based on these assumptions, we will add the following properties to the **Order** entity model for harmonization:
+
+ | Name | Type | Other settings | Notes |
+ |:---:|:---:|:---:|---|
+ | `id` | string | **** **** | Used as the primary key because order ID is unique for each order. Needs an element range index. |
+ | `total` | decimal | | The calculated total amount of the entire order. |
+ | `products` | **Product** entity | Cardinality: 1..∞ | An array of pointers to the `Product` entities in our FINAL database. |
+ {:.table-b1gray}
+
+{% include conrefs/conref-qs-4x-define-entity-model.md imgpath=var-imgpath entityname="Order" fullsteps=true %}
+
+### Result
+
+Because the **Order** entity contains pointers to the **Product** entity, an arrow connects the **Order**{:.uilabel} entity card to the **Product**{:.uilabel} entity card with the cardinality we selected (1..∞).
+
+ {%- assign full-imgpath=var-imgpath | append: "qs-4x-entities-order-card-to-product-card.png" -%}{% include thumbnail.html imgfile=full-imgpath alttext="" imgclass="img-results" tab=" " %}
+
+
+## 2 - Create the Harmonize Flow
+
+Harmonization uses the data in your **STAGING** database to generate canonical entity instances (documents) in the **FINAL** database.
+
+{% include conrefs/conref-qs-4x-create-run-harmonize-flow.md imgpath=var-imgpath entityname="Order" harmonizeflowname="Harmonize Orders" create=true fullsteps=true %}
+
+Because we used the default **Create Structure from Entity Definition**{:.uilabel} and we did not specify a mapping, DHF creates boilerplate code based on the entity model. This code includes default initialization for the entity properties, which we will customize.
+
+
+## 3 - Customize the Harmonize Flow
+
+### 3a - Customize the Collector Plugin
+
+The **Collector** plugin generates a list of IDs for the flow to operate on. The IDs can be whatever your application needs (e.g., URIs, relational row IDs, twitter handles). The default **Collector** plugin produces a list of source document URIs.
+
+An `options` parameter is passed to the **Collector** plugin, and it contains the following properties:
+
+ - **entity**: the name of the entity this plugin belongs to (e.g., "Order")
+ - **flow**: the name of the flow this plugin belongs to (e.g., "Harmonize Orders")
+ - **flowType**: the type of flow being run ("input" or "harmonize"; e.g., "harmonize")
+
+The **Load Orders** input flow automatically groups the source documents into a collection named **Order**. The default **Collector** plugin uses that collection to derive a list of URIs.
+
+ View code snippet.
+ ```javascript
+ cts.uris(null, null, cts.collectionQuery(options.entity))
+ ```
+
+
+In our source **Order** CSV file, each row represented one line item in an order. For example, if the order had three line items, then three documents were created for that order in the staging database during the input phase. To combine all three documents into a single **Order** entity, they must be harmonized.
+
+Each of those three documents would have the same order ID but different URIs. Therefore, we must customize the collector plugin to return a list of unique order IDs, instead of a list of URIs.
+
+**Technical Notes**
+
+ - In our custom collector plugin code, we use the [jsearch library](https://docs.marklogic.com/guide/search-dev/javascript) library to find all the values of **id** in the **Order** collection and return the result.
+ - By default, jsearch paginates results; therefore, we call `slice()` to get all results at once.
+
+**Steps**
+
+To customize the **Collector** plugin,
+
+{% assign full-imgpath=var-imgpath | append: "qs-4x-flows-harmonize-collector-custom-Order.png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Harmonize Flow - Collector - custom code" imgclass="screenshot" tab=" " %}
+
+1. Click the **COLLECTOR**{:.uilabel} tab.
+1. Replace the collector plugin code with the following:
+
+1. Click **SAVE**{:.inline-button}.
+{:.ol-steps}
+
+
+### 3b - Customize the Content Plugin
+
+The list of order IDs collected by our custom **Collector** plugin is passed to the **Content** plugin, specifically to its `createContent` function.
+
+We will customize `createContent` to do the following:
+
+ - Collect all the line items of the same order into a single **Order** entity.
+ - Calculate the total cost of the order.
+
+**Technical Notes**
+
+ - A [jsearch library](https://docs.marklogic.com/guide/search-dev/javascript) query searches the **Order** collection for all source documents that have the same order id.
+
+ We also apply a `map` function to each matching document to extract the original content inside the envelope.
+
+ The `orders` variable will contain an array of original JSON objects.
+
+ View code snippet.
+
+var orders = jsearch
+ .collections('Order')
+ .documents()
+ .where(
+ jsearch.byExample({
+ 'id': id
+ })
+ )
+ .result('value')
+ .results.map(function(doc) {
+ return doc.document.envelope.instance;
+ });
+
+
+
+ - After collecting the line items in the same order,
+
+ - We calculate the total amount of the order and
+ - We store the appropriate **Product** entity references (using the SKU) in the **products** property of the **Order** instance.
+
+ View code snippet.
+
+/* The following property is a local reference. */
+var products = [];
+var price = 0;
+for (var i = 0; i < orders.length; i++) {
+ var order = orders[i];
+ if (order.sku) {
+ products.push(makeReferenceObject('Product', order.sku));
+ price += xs.decimal(parseFloat(order.price)) * xs.decimal(parseInt(order.quantity, 10));
+ }
+}
+
+
+
+ - The default code includes some additional functions that we will remove because we do not need them.
+
+ - `extractInstanceProduct`: Extracts a Product instance in a form suitable for insertion into an Order instance. Because we reference Product entities within the Order instance, we do not need this function.
+ - `extractInstanceOrder`: Extracts an Order instance from an order source document. Since we do not have a one-to-one correspondence, we cannot use this function.
+
+ However, although we do not use `extractInstanceOrder`, our customized `createContent` function must produce a similar structure.
+
+ View code snippet.
+
+return {
+ '$attachments': attachments,
+ '$type': 'Order',
+ '$version': '0.0.1',
+ 'id': id,
+ 'price': price,
+ 'products': products
+}
+
+
+
+**Steps**
+
+To customize the content plugin code,
+
+{% assign full-imgpath=var-imgpath | append: "qs-4x-flows-harmonize-content-custom-Order.png" %}
+{% include thumbnail.html imgfile=full-imgpath alttext="Harmonize Flow - Content - custom code" imgclass="screenshot" tab=" " %}
+
+1. Click the **CONTENT**{:.uilabel} tab.
+1. Replace the content plugin code with the following:
+
+1. Click **SAVE**{:.inline-button}.
+{:.ol-steps}
+
+
+## 4 - Run the Harmonize Flow
+
+{% include conrefs/conref-qs-4x-create-run-harmonize-flow.md imgpath=var-imgpath entityname="Order" harmonizeflowname="Harmonize Orders" run=true fullsteps=true %}
+
+
+## 5 - View the Harmonized Orders
+
+As with other flow runs, you can view the job status.
+
+{% include conrefs/conref-qs-4x-jobs.md imgpath=var-imgpath pickitem="the job for the harmonization flow `Harmonize Orders`" %}
+
+You can also explore your harmonized data in the **FINAL** database.
+
+{% include conrefs/conref-qs-4x-browse-data.md imgpath=var-imgpath pickdb="FINAL" pickitem="the first **Order** dataset item" %}
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/harmonize-product-data-by-mappings.md b/_pages/tutorial/4x/harmonize-product-data-by-mappings.md
new file mode 100644
index 0000000000..6823815374
--- /dev/null
+++ b/_pages/tutorial/4x/harmonize-product-data-by-mappings.md
@@ -0,0 +1,68 @@
+---
+layout: inner
+title: Tutorial - Harmonize the Product Data by Mappings
+permalink: /tutorial/4x/harmonize-product-data-by-mappings/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Harmonize the Product Data by Mapping
+
+A **harmonize flow** is another series of plugins that harmonizes the data in the staging database and stores the results in the final database. Harmonization includes standardizing formats, enriching data, resolving duplicates, indexing, and other tasks.
+
+We can specify the source of an entity property value using one of two methods:
+ - By customizing the default harmonization code.
+ - By defining mappings that specify which fields in the raw datasets correspond with which properties in the entity model.
+
+Model-to-model mapping (between the source data model and the canonical entity model) was [introduced in DHF v4.0.0]({{site.baseurl}}/release-notes/release-notes-4_0_x/) to enable users to easily create a harmonization flow without coding. Mappings are ideal when the source data can be easily converted for use as the value of the entity property; a simple conversion can be a difference in the label case or a difference in simple data types.
+
+
+We have already loaded the **Product** raw data by:
+ - [creating the **Product** entity]({{site.baseurl}}/tutorial/4x/create-entities/) and
+ - [creating and running the associated input flow]({{site.baseurl}}/tutorial/4x/create-run-input-flows/).
+
+In this section, we will:
+ - [Define the entity model](#1_-_define_entity_model) by adding properties to the entity model.
+ - [Define the mappings](#2_-_define_the_mappings) to specify which field in the dataset corresponds to the properties in the entity model.
+ - [Create and Run the Harmonize Flow.](#3_-_create_and_run_the_harmonize_flow)
+
+
+## 1 - Define the Entity Model
+
+We first define the entity model, which specifies the standard labels for the fields we want to harmonize. For the **Product** dataset, we will harmonize two fields: `sku` and `price`. Therefore, we must add those fields as properties to our **Product** entity model.
+
+ | Name | Type | Other settings | Notes |
+ |:---:|:---:|:---:|---|
+ | `sku` | string | key | Used as the primary key because the SKU is unique for each product. |
+ | `price` | decimal | | Set as a decimal because we need to perform calculations with the price. |
+ {:.table-b1gray}
+
+{% include conrefs/conref-qs-4x-define-entity-model.md imgpath=var-imgpath entityname="Product" fullsteps=true %}
+
+
+## 2 - Define the Mappings
+
+For the **Product** entity, we define the following simple mappings:
+
+ | field in raw dataset (type) | property in entity model (type) | Notes |
+ |---|---|---|
+ | `SKU` (string) | `sku` (string) | Difference (case-sensitive) between field names |
+ | `price` (string) | `price` (decimal) | Difference in types |
+ {:.table-b1gray}
+
+{% include conrefs/conref-qs-4x-define-source-to-entity-maps.md imgpath=var-imgpath entityname="Product" mappingname="Product Mapping" fullsteps=true %}
+
+
+## 3 - Create and Run the Harmonize Flow
+
+Harmonization uses the data in your **STAGING** database to generate canonical entity instances in **FINAL** database.
+
+{% include conrefs/conref-qs-4x-create-run-harmonize-flow.md imgpath=var-imgpath entityname="Product" harmonizeflowname="Harmonize Products" mappingname="Product Mapping" create=true run=true fullsteps=true %}
+
+
+## See Also
+- [Using Model-to-Model Mapping]({{site.baseurl}}/harmonize/mapping/)
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/harmonizing-order-data.md b/_pages/tutorial/4x/harmonizing-order-data.md
deleted file mode 100644
index fa50a2df56..0000000000
--- a/_pages/tutorial/4x/harmonizing-order-data.md
+++ /dev/null
@@ -1,186 +0,0 @@
----
-layout: inner
-title: Tutorial - Harmonize the Order Data
-permalink: /tutorial/4x/harmonizing-order-data/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Harmonize the Order Data
-
-Now that we have modeled the Order entity we can use the Data Hub Framework's code scaffolding to generate harmonization code and then customize it for our application.
-
-Recall that you can either generate customized harmonization code based on a model-to-model mapping, as we did with Product, or you can generate default harmonization code and customize it. In this exercise, we will generate and customize the default code since the **price** property of an Order is a computed value.
-
-## Create the Order Harmonize Flow
-
-Follow these steps to create a harmonize flow for Order entities and generate the default flow code:
-
-1. Click **Flows** in the top navigation bar.
-1. Click the **+** icon next to **Harmonize Flows**
-1. Type **Harmonize Orders** in the **Harmonize Flow Name** field
-1. Click **Create**.
-
-The following picture summarizes these steps:
-
-
-
-Since we used the default option of **Create Structure from Entity Definition** and did not specify a mapping, the Data Hub Framework creates boilerplate code based on the Order entity model. The code includes default initialization for the entity properties, which we will modify after a brief overview of the harmonization code and the server-side plugins that use it.
-
-## Harmonization Flow Basics
-
-Harmonize flows are designed to operate on your data in batches. A set of plugins on MarkLogic orchestrate the processing and provide hooks for domain-specific code. When you create a flow, the Data Hub Framework generates harmonization code that the plugins call into when you run the flow.
-
-The framework also [provides ways]({{site.baseurl}}/refs/faqs/#how-can-i-run-a-harmonize-flow-immediately-for-1-document) to run a harmonize flow on-demand for single items.
-
-A harmonization flow uses the following plugins:
-
-- **main**: orchestrates the behavior of the other plugins
-- **collector**: returns a list of strings to operate on
-- **content**: returns data to put into the content section of the envelope
-- **headers**: returns data to put into the headers section of the envelope
-- **triples**: returns data to put into the triples section of the envelope
-- **writer**: receives the final envelope and writes it to the database.
-
-The main plugin receives id values from the collector and orchestrates the behavior of the other plugins. The collector plugin returns a list of things to operate on. The Data Hub Framework then breaks the list of things into parallel batches of a configurable size and sends each item to the content, headers, triples, and writer plugins, in turn, as a transaction.
-
-The writer plugin acts last. By default, the writer inserts the envelope into the FINAL database, but you can do whatever you like. For example, you could push the envelope on to a message bus or send a tweet.
-
-The following diagram shows the steps in a harmonize flow:
-
-
-
-In this exercise, we will examine and customize the harmonization code for the collector and content plugins.
-
-When you create a flow, the Data Hub Framework generates harmonize code to be run by the plugins. You can review and modify the code using the tabs of the Quickstart **Flows** view. For example, if you click on the Harmonize Orders flow on the left, you see the following:
-
-
-
-## Customize the Collector Plugin Code
-
-Click the **COLLECTOR** tab to view the collector plugin code. We will discuss the cusotmizations and then apply them.
-
-The Data Hub Framework calls the `collect` function to "collect" a list of ids for the flow to operate on. By default, this function generates a list of source document URIs. The ids can be anything: URIs, relational row ids, twitter handles - whatever suits the needs of your application.
-
-The `options` parameter passed into the collector plugin contains the following properties by default:
-
-- **entity**: the name of the entity this plugin belongs to ("Order")
-- **flow**: the name of the flow this plugin belongs to ("Harmonize Orders")
-- **flowType**: the type of flow being run ("input" or "harmonize"; in this case, "harmonize")
-
-The "Load Orders" input flow automatically grouped the source documents into a collection named "Order". The default code uses that collection to derive a list of URIs:
-```javascript
-cts.uris(null, null, cts.collectionQuery(options.entity))
-```
-This approach worked for the Product entity type. However, the order source data in the **STAGING** database consists of one document for each item that occurs in an order. During harmonization, we want to collect all the items in the same order into a single Order entity.
-
-Therefore, we will change the collector to generate a list of the unique order ids, instead of a list of URIs. The code below uses the [jsearch library](https://docs.marklogic.com/guide/search-dev/javascript) library to find all the values of **id** in the Order collection:
-
-```javascript
-jsearch
- .values('id')
- .where(cts.collectionQuery(options.entity))
- .slice(0, Number.MAX_SAFE_INTEGER)
- .result();
-```
-
-By default jsearch will paginate results. The `slice()` call tells jsearch to return all results from 0 to a really big number.
-
-Now, we'll apply our customization: Replace the contents of the COLLECTOR tab with the following code, and click **SAVE**.
-
-
-
-## Customize the Content Plugin Code
-Click the **CONTENT** tab to bring up the content plugin code. We will discuss the cusotmizations and then apply them.
-
-The `createContent` function is the main entry point for the content plugin. Because of our collector plugin customization, the **id** parameter passed to this function will be an order id generated by the collector plugin.
-
-We will modify `createContent` to collect all the items that are part of the same order into a single Order entity. In addition, we will calculate the total cost of the order.
-
-We will use the [jsearch library](https://docs.marklogic.com/guide/search-dev/javascript) to find the relevant source documents. The following query finds all order source documents with a matching order id:
-```
-var orders = jsearch
- .collections('Order')
- .documents()
- .where(
- jsearch.byExample({
- 'id': id
- })
- )
- .result('value')
- .results.map(function(doc) {
- return doc.document.envelope.instance;
- });
-```
-
-We apply a `map` function to the matching documents to extract the original content that the input flow stored in the instance part of the envelope. The `orders` variable will contain an array of original JSON objects.
-
-Given all the products in the same order, the following code sums up the prices and adds a reference to each Product in the order to the **products** property of the Order instance. The products are included by reference, using the **sku** to identify each product.
-```
-/* The following property is a local reference. */
-var products = [];
-var price = 0;
-
-for (var i = 0; i < orders.length; i++) {
- var order = orders[i];
- if (order.sku) {
- products.push(makeReferenceObject('Product', order.sku));
- price += xs.decimal(parseFloat(order.price)) * xs.decimal(parseInt(order.quantity, 10));
- }
-}
-```
-The default code includes some additional functions that we will remove because we do not need them.:
-* `extractInstanceProduct`: Extracts a Product instance in a form suitable for inlining in an Order instance. Since we include Product entities by reference, we discard this function.
-* `extractInstanceOrder`: Extracts an Order instance from an order source document. Since we do not have a one-to-one correspondence, we cannot use this function.
-
-Though we do not use `extractInstanceOrder`, our customized `createContent` function must produce the same kind of structure, so we hoist the following block of code into `createContent`:
-```
-return {
- '$attachments': attachments,
- '$type': 'Order',
- '$version': '0.0.1',
- 'id': id,
- 'price': price,
- 'products': products
-}
-```
-
-Now, we'll apply our customizations: Replace the contents of the **CONTENT** tab with the following code and click **SAVE**.
-
-
-
-## Run the Harmonize Flow
-
-Next, run the flow using the following steps:
-
-1. Click the **FLOW INFO** tab.
-1. Click **RUN HARMONIZE** to start the flow.
-
-
-
-## View the Harmonized Orders
-
-Similar to what we did after running the other flows, you might want to verify that the job finished.
-
-1. Click **Jobs** in the top navigation bar.
-1. Confirm the harmonize job status is FINISHED.
-
-
-
-You might also want to explore your harmonized data.
-
-1. Click **Browse Data**.
-1. Change the database to **Final**.
-1. Click the **Order** facet to filter the results.
-
-You should see harmonized documents in the search results.
-
-
-
-Click a result to see the raw data.
-
-{:.screenshot-border}
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/harmonizing-product-data.md b/_pages/tutorial/4x/harmonizing-product-data.md
deleted file mode 100644
index 0318d2213e..0000000000
--- a/_pages/tutorial/4x/harmonizing-product-data.md
+++ /dev/null
@@ -1,78 +0,0 @@
----
-layout: inner
-title: Tutorial - Harmonize the Product Data
-permalink: /tutorial/4x/harmonizing-product-data/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Harmonize the Product Data
-
-Now that we have modeled the Product entity type and defined a model-to-model mapping for it, we can use the Data Hub Framework to harmonize our source data with our entity model. Harmonization creates canonical entity instances containing the necessary parts of your source data.
-
-Recall from earlier that the Data Hub Framework can use the Entity Services model definition and a mapping to generate harmonization code. In this exercise, we will do the following:
-
-1. Create a harmonize flow that uses a model-to-model mapping to guide harmonization code generation.
-1. Run the flow to create harmonized Product entities in the FINAL database.
-
-To begin, click **Flows** in the top navigation bar.
-
-
-
-## Create the Harmonize Flow
-
-Use the following procedure to create a Product harmonize flow:
-
-1. Click **+** next to **Harmonize Flows**.
-1. Type **Harmonize Products** in the **Harmonize Flow Name** field.
-1. Click on **ProductMapping** under Mapping Generation.
-1. Click **CREATE**. A new harmonize flow is created.
-
-The following picture summarizes the steps for creating the Harmonize Products flow:
-
-
-When you create the flow, QuickStart generates harmonization code based on the Product entity model and the **ProductMapping** model-to-model mapping, and then deploys the code to MarkLogic.
-
-Though you can customize the harmonization code, it is not necessary. The mapping expressed everything needed to create Product entities. For example, the mapping caused the generated code to include the following assignments for initializing a Product instance:
-```
-/* These mappings were generated using mapping: ProductMapping, version: 1 on ... */
-let sku = !fn.empty(source.xpath('//SKU')) ? xs.string(fn.head(source.xpath('//SKU'))) : null;
-let price = !fn.empty(source.xpath('//price')) ? xs.decimal(fn.head(source.xpath('//price'))) : null;
-```
-We'll dig deeper into the generated code when we work with our second entity later in this tutorial.
-
-## Run the Flow
-
-When you run a harmonization flow, the Data Hub Framework uses the data in your **STAGING** database to generate canonical entity instances in **FINAL** database.
-
-Use the following procedure to run a flow:
-
-1. Click the **Flow Info** tab if you are not already on that tab.
-1. Click **Run Harmonize** to start the flow. A pop-up appears at the bottom of the page indicating your harmonization job has started.
-
-When harmonization completes, a notification pop-up appears at the bottom of the page.
-
-## Check the Harmonized Job Status
-
-Recall that we verified the job status after running the input flow. We will now do the same thing for the harmonize flow.
-
-1. Click **Jobs** in the top navigation bar to view your jobs.
-1. Verify the harmonization job appears in the list and has a FINISHED status.
-
-
-
-## Explore the Harmonized Data
-
-To explore the harmonized data:
-
-1. Click **Browse Data** in the top navigation bar to display the data browser.
-1. Select **Final** in the database dropdown. The search results show the harmonized documents.
-1. Click a result to see the harmonized data. For example:
-
-{:.screenshot-border}
-
-Congratulations! You just loaded and harmonized your product data. Up next is doing the same thing for the order data.
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/mapping-product-entity.md b/_pages/tutorial/4x/mapping-product-entity.md
deleted file mode 100644
index f7ceb4677e..0000000000
--- a/_pages/tutorial/4x/mapping-product-entity.md
+++ /dev/null
@@ -1,73 +0,0 @@
----
-layout: inner
-title: Tutorial - Create a Product Source-to-Entity Mapping
-permalink: /tutorial/4x/mapping-product-entity/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Create a Product Source-to-Entity Mapping
-
-This exercise walks you through creating a mapping for the Product entity.
-
-### Model to Model Mapping Introduction
-The Data Hub Framework enables you to control the source of an entity property value in two ways:
-* Define a mapping from source to entity property that can be used to generate customized harmonization code.
-* Customize the default code harmonization code yourself.
-
-In many cases, a value in your source data model can be mapped directly on to its corresponding canonical entity property, perhaps with a simple type conversion. Data Hub Framework calls this _model to model mapping_ because you are mapping data from your (inferred) source data model to your canonical entity model.
-
-A mapping enables you to quickly and easily create a harmonization flow without writing or modifying code.
-
-You can create a mapping using QuickStart's mapping tool. QuickStart generates a list of possible source JSON property names by examining one of your source documents. Then, you use the UI to specify which source properties map to which entity properties.
-
-To learn more about model to model mapping, see [Using Model-to-Model Mapping]({{site.baseurl}}/harmonize/mapping/).
-
-### Product Mapping Requirements
-We want to define a mapping that maps the **SKU** JSON property in the source data on to the **sku** entity property. The value is represented as a string in both source and model.
-
-Similarly, we want to map the **price** JSON property on to the **price** entity property. In the source data, the price is stored as a string, but **price** has decimal type in our Product model.
-
-### Create the Product Mapping
-
-To get started with mapping, click **Mapping** in the top navigation bar.
-
-
-
-Follow these steps to create a new mapping:
-
-1. Click **+** next to **Product** in the left sidebar. The Mapping Creation dialog appears.
-1. Type **ProductMapping** in the **Mapping Name** field.
-1. Click **CREATE**. The mapping editor appears.
-
-The following picture summarizes the steps for creating a mapping:
-
-
-
-The mapping editor displays a row for each property in your entity model. The right column in each row is the name of an entity property. The left column in each row is a dropdown list from which you can select the source property to map on to the entity property in the right column.
-
-QuickStart generates the list of source property names by examining one of your source documents in the STAGE database. You can view the URI of the selected document at the top of the **Source** column of the mapping editor.
-
-You change the source document by clicking on the pencil icon next to the source URI. To find an alternative document URI, use the **Browse Data** view to review the documents in the "Product" collection in the **STAGING** database.
-
-The following diagram illustrates key parts of the mapping editor:
-
-{:.screenshot-border}
-
-Next, map the **sku** and **price** properties using the following steps:
-
-1. Click the dropdown in the **Source** column of the **sku** row. You see a list of the JSON property names found in the source document, along with the data type and an example value of each.
-1. Scroll down or type into the text box at the top of the list to locate **SKU**, then select it.
-1. Click the dropdown in the **Source** column of the **price** row.
-1. Find and select **price** in the dropdown list.
-1. Click **SAVE MAP** in the upper right corner to save your changes.
-
-Your final Product mapping should look like the following:
-
-{:.screenshot-border}
-
-Next, we will create a harmonization flow that uses the mapping, and then run it to harmonize the product data.
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/modeling-order-entity.md b/_pages/tutorial/4x/modeling-order-entity.md
deleted file mode 100644
index 797e39aeea..0000000000
--- a/_pages/tutorial/4x/modeling-order-entity.md
+++ /dev/null
@@ -1,100 +0,0 @@
----
-layout: inner
-title: Tutorial - Model the Order Entity
-permalink: /tutorial/4x/modeling-order-entity/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Model the Order Entity
-
-## Determine What to Model
-
-If we were developing a production application, we would now explore the order data to understand it better before making entity modeling decisions. Let's skip ahead and pretend that we have spoken to our company's data guru again and have learned the following about the order data:
-
-* Each order can have more than one product.
-* Each product in an order has a variable quantity.
-* Each product is identified by its sku.
-* Each order should include a total price, computed from the per product prices and quantities.
-
-From this, we know we want to harmonize three fields: **id**, **price**,
-and **products**.
-
-* **id** is the unique identifier of each order.
-* **price** is the total price of the entire order.
-* **products** will be an array of pointers to the Product entities in our Final database.
-
-Let's look at the first order, which has an id equal to 1.
-
-~~~
-id,customer,order_date,ship_date,product_id,sku,price,quantity,discounted_price,title,description
-1,600,04/20/2017,05/02/2017,1000055,101591922267,4.5,1.0,3.92,different gallon,
-1,600,04/20/2017,05/02/2017,1000153,164451986229,4.5,1.0,3.92,unwilling eave,
-1,600,04/20/2017,05/02/2017,1000066,118675935929,33.99,2.0,32.53,stingy sharon,
-~~~
-{: .language-csv}
-
-For purposes of our entity modeling, this order contains the following key pieces of information:
-
-| SKU | Quantity | Item Price |
-| :--- | :---: | ---: |
-| 101591922267 | 1.0 | 4.5 |
-| 164451986229 | 1.0 | 4.5 |
-| 118675935929 | 2.0 | 33.99 |
-
-From this information, we can compute the price of the order as follows:
-
-~~~
-(4.5 * 1) +
-(4.5 * 1) +
-(33.99 * 2)
------------
-$76.98
-~~~
-
-Now, we can model our Order entity.
-
-1. Click **Entities** in the top navigation bar.
-1. Open the entity editor by clicking on the **pencil** icon () in the upper right corner of the Order entity.
-
- 
-
-## Add the **id** Property
-1. Click **+**{:.circle-button} below **Properties** to add a new property.
-1. Click the area just below the **key** icon () to make **id** the primary key.
-1. Click the area just below the **lightning bolt** icon () to specify the need for an element range index for this property.
-1. Enter **id** as the Name.
-1. Select **string** as the Type.
-
-The following pictures summarize the steps for adding the **id** property:
-
-
-
-## Add the **price** Property
-
-1. Click **+**{:.circle-button} below **Properties** to add another property.
-1. Enter **price** as the Name.
-1. Select **decimal** as the Type.
-
-## Add the **products** Property
-
-1. Click **+**{:.circle-button} below **Properties** to add another property.
-1. Enter **products** as the Name.
-1. Select **Product** as the Type.
-1. Select **1..∞** as the Cardinality. This indicates an Order can contain more than one Product.
-
-Your final Order properties definitions should look like the following:
-
-
-
-Click **Save**. When asked whether or not to update indexes in MarkLogic, click **Yes**.
-
-You should now see Order and Product entity definitions similar to the following. If you do not see the properties for an entity, click and drag the lower right corner of the entity definition box to resize it.
-
-
-
-One of the benefits of having the Data Hub Framework model your data with Entity Services is that it can use the model to create database configuration options automatically. This means it can update the necessary index settings based on how you model your data.
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/modeling-product-entity.md b/_pages/tutorial/4x/modeling-product-entity.md
deleted file mode 100644
index fc3ee9438a..0000000000
--- a/_pages/tutorial/4x/modeling-product-entity.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-layout: inner
-title: Tutorial - Model the Product Entity
-permalink: /tutorial/4x/modeling-product-entity/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Model the Product Entity
-
-Harmonization standardizes data labels and formats among different datasets. For example, "family name" in one dataset could be called "last name" in another, and harmonization can allow both to be accessed as "surname".
-
-For the **Product** dataset, we will harmonize two fields: `sku` and `price`. However, before we can harmonize the data, we must add those fields as properties to our **Product** entity model.
-
- - Because SKU is unique for each product, we will use `sku` as the primary key.
- - Because we need to perform calculations with the price, we will set `price` as a decimal.
-
-{% assign full-imgpath=var-imgpath | append: "qs-4x-entities-edit-properties-sku-price.png" %}
-{% include thumbnail.html imgfile=full-imgpath alttext="Product Entity properties" imgclass="screenshot" tab=" " %}
-
-1. In QuickStart's navigation bar, click **Entities**{:.uimenuitem}.
-1. At the top of the `Product` entity card, click the pencil icon ****{:.circle-button} to edit the `Product` entity definition.
-1. In the `Product` entity editor, click **+**{:.circle-button} in the **Properties**{:.uilabel} section to add a new property.
-1. Set **Name**{:.uilabel} to `sku`.
-1. Set **Type**{:.uilabel} to `string`.
-1. To make `sku` the primary key, click the area in the key ****{:.circle-button} column for the `sku` row.
-1. Click **+**{:.circle-button} again to add another property.
-1. Set **Name**{:.uilabel} to `price`.
-1. Set **Type**{:.uilabel} to `decimal`.
-1. Click **SAVE**{:.inline-button}.
-{%- assign full-imgpath=var-imgpath | append: "qs-4x-update-indexes-yes.png" -%}{%- assign full-text="If prompted to update the index, click Yes." -%}{%- include step-collapsed.html steptext=full-text stepimg=full-imgpath imgclass="img-small" -%}
-{:.ol-steps}
-
-QuickStart updates the index settings based on how you model your data. A benefit of modeling your data with Entity Services is that you can use the model to create database configuration options automatically.
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/secure-pii-in-customer-data.md b/_pages/tutorial/4x/secure-pii-in-customer-data.md
new file mode 100644
index 0000000000..4ffaaf3096
--- /dev/null
+++ b/_pages/tutorial/4x/secure-pii-in-customer-data.md
@@ -0,0 +1,69 @@
+---
+layout: inner
+title: Tutorial - Secure Personally Identifiable Information
+permalink: /tutorial/4x/secure-pii-in-customer-data/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Securing Personally Identifiable Information
+
+Securing personally identifiable information (PII) was [introduced in DHF v4.0.0]({{site.baseurl}}/release-notes/release-notes-4_0_x/). To protect PII, the PII fields must be identified in the entity model. Then QuickStart automatically generates PII security configuration files, which we will deploy to the **FINAL** database.
+
+We have already loaded the **Customer** raw data by:
+ - [creating the **Customer** entity]({{site.baseurl}}/tutorial/4x/create-entities/) and
+ - [creating and running the associated input flow]({{site.baseurl}}/tutorial/4x/create-run-input-flows/).
+
+In this section, we will:
+ - [Define the entity model](#1_-_define_the_entity_model) by adding properties to the entity model.
+ - [Define the source-to-entity mapping](#2_-_define_the_mappings) to specify which field in the dataset corresponds to the properties in the entity model.
+ - [Create and run the Harmonize Flow.](#3_-_create_and_run_the_harmonize_flow)
+ - [Deploy the configuration files.](#4_-_deploy_the_configuration_files)
+
+
+## 1 - Define the Entity Model
+
+To simplify this tutorial, we are going to harmonize only the primary key (`id`) and two fields that we choose to protect as PII (`billing_address` and `shipping_address`).
+
+ | Name | Type | Other settings | Notes |
+ |:---:|:---:|:---:|---|
+ | `id` | string | key | Unique for each customer. |
+ | `billing_address` | string | PII | |
+ | `shipping_address` | string | PII | |
+ {:.table-b1gray}
+
+{% include conrefs/conref-qs-4x-define-entity-model.md imgpath=var-imgpath entityname="Customer" fullsteps=true %}
+
+
+## 2 - Define the Mappings
+
+Because the information can easily be mapped between the source dataset and the entity model, we will create the following source-to-entity mappings:
+
+ | field in raw dataset (type) | property in entity model (type) | Notes |
+ |---|---|---|
+ | `id` (string) | `id` (string) | No changes |
+ | `billing_address` (string) | `billing_address` (string) | No changes |
+ | `shipping_address` (string) | `shipping_address` (string) | No changes |
+ {:.table-b1gray}
+
+{% include conrefs/conref-qs-4x-define-source-to-entity-maps.md imgpath=var-imgpath entityname="Customer" mappingname="Customer Mapping" fullsteps=true %}
+
+
+## 3 - Create and Run the Harmonize Flow
+
+Harmonization uses the data in your **STAGING** database to generate canonical entity instances in **FINAL** database.
+
+{% include conrefs/conref-qs-4x-create-run-harmonize-flow.md imgpath=var-imgpath entityname="Customer" harmonizeflowname="Harmonize Customers" mappingname="Customer Mapping" create=true run=true fullsteps=true %}
+
+
+## 4 - Deploy the Configuration Files
+
+To deploy the PII security configuration files to the **FINAL** database,
+
+1. Open a command-line window, and navigate to your DHF project root directory.
+1. {% include ostabs-run-gradle-step.html grtask="mlDeploySecurity" %}
+{:.ol-steps}
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/securing-pii.md b/_pages/tutorial/4x/securing-pii.md
deleted file mode 100644
index 10b6d0508b..0000000000
--- a/_pages/tutorial/4x/securing-pii.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-layout: inner
-title: Tutorial - Securing Personally Identifiable Information
-permalink: /tutorial/4x/securing-pii/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Securing Personally Identifiable Information
-
-To protect personally identifiable information (PII), we will identify them in QuickStart, which automatically generates PII security configuration files. Then we will deploy these configuration files to the **FINAL** database.
-
-
-## Identify PII
-
-{% include conrefs/conref-qs-4x-identify-pii.md imgpath=var-imgpath entityname="Order" %}
-
-
-## Deploy Configuration Files
-
-To deploy the PII security configuration files to the **FINAL** database,
-
-1. Open a command-line window, and navigate to your DHF project root directory.
-1. {% include ostabs-run-gradle-step.html grtask="mlDeploySecurity" %}
-{:.ol-steps}
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/serve-data.md b/_pages/tutorial/4x/serve-data.md
deleted file mode 100644
index c24443146a..0000000000
--- a/_pages/tutorial/4x/serve-data.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-layout: inner
-title: Tutorial - Serve the Data Out of MarkLogic
-permalink: /tutorial/4x/serve-data/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Serve the Data Out of MarkLogic
-
-
-
-You have just successfully loaded two data sources and harmonized them both.
-
-Now you can access your data via several REST endpoints. Your harmonized data is available on the Final HTTP server on port 8011 by default. A full list of REST endpoints is described in the [Client API documentation](https://docs.marklogic.com/REST/client){:target="_blank"}.
-
-You can access hub data in the Staging database via the [Staging search endpoint](http://localhost:8010/v1/search?format=json){:target="_blank"} and in the Final database via the [Final search endpoint](http://localhost:8011/v1/search?format=json){:target="_blank"}.
-
-Pictured here is the Final search endpoint:
-
-{:.screenshot-border}
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/takeaways.md b/_pages/tutorial/4x/takeaways.md
new file mode 100644
index 0000000000..ae2ca85663
--- /dev/null
+++ b/_pages/tutorial/4x/takeaways.md
@@ -0,0 +1,41 @@
+---
+layout: inner
+title: Tutorial - Takeaways
+permalink: /tutorial/4x/takeaways/
+---
+
+{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
+
+
+# Tutorial: Takeaways
+
+In this tutorial, you learned the full process of ingesting and harmonizing data from your source datasets into a data hub in the MarkLogic Server with variations in handling more complex data and personally identifiable information (PII).
+
+**QuickStart is intended for use in a development environment only.** For production, use MLCP or other tools for input flows and gradle tasks or your own code (using the provided [Java classes]({{site.baseurl}}/harmonize/java/)) for harmonization flows. See [DHF Tools]({{site.baseurl}}/tools/).
+
+
+## Clean Up
+
+To delete one or more databases created by this tutorial,
+1. In QuickStart's navigation bar, click **Dashboard**{:.uimenuitem}.
+2. In the **Databases**{:.uilabel} section, click the trash icon for the database you want to delete.
+3. To delete all database, click the skull icon at the top right of the **Databases**{:.uilabel} section.
+{:.ol-steps}
+
+To delete the entire data hub created by this tutorial and to uninstall DHF,
+1. In QuickStart's navigation bar, click **Settings**{:.uimenuitem}.
+1. Select **Uninstall Hub**{.uilabel}.
+{:.ol-steps}
+
+
+## Additional Resources
+
+- [MarkLogic University](https://mlu.marklogic.com/ondemand/index.xqy?q=Series%3A%22Operational%20Data%20Hubs%22): Data Hub Framework On Demand Video Courses
+- [Data Hub Framework Examples](https://github.com/marklogic/marklogic-data-hub/tree/develop/examples): Many examples of how to use the Data Hub Framework
+
+**Questions:** Use the [#marklogic-dhf tag on StackOverflow](https://stackoverflow.com/questions/ask?tags=marklogic-dhf).
+
+**Comments/Bugs:** [File an issue on Github](https://github.com/marklogic/marklogic-data-hub/issues/new).
+
+
+{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/4x/view-jobs.md b/_pages/tutorial/4x/view-jobs.md
index 3693b3120f..193391f253 100644
--- a/_pages/tutorial/4x/view-jobs.md
+++ b/_pages/tutorial/4x/view-jobs.md
@@ -11,7 +11,7 @@ permalink: /tutorial/4x/view-jobs/
We use the QuickStart Jobs viewer to view the results of the jobs we just ran.
-{% include conrefs/conref-qs-4x-jobs.md imgpath=var-imgpath pickitem="the job for the input flow `Load Products`" %}
+{% include conrefs/conref-qs-4x-jobs.md imgpath=var-imgpath pickitem="the job for the input flow `Load Products`" fullsteps=true %}
### Result
diff --git a/_pages/tutorial/4x/wrapping-up.md b/_pages/tutorial/4x/wrapping-up.md
deleted file mode 100644
index bf8bf0540e..0000000000
--- a/_pages/tutorial/4x/wrapping-up.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-layout: inner
-title: Tutorial - Wrapping Up
-permalink: /tutorial/4x/wrapping-up/
----
-
-{% assign var-imgpath = site.baseurl | append: "/images/4x/" %}
-
-
-# Tutorial: Wrapping Up
-
-## Congratulations!
-You just created a data hub.
-
-- You loaded two separate data sources from CSV files.
-- You modeled two entities.
-- You harmonized your data based on the entity definitions.
-- Your data is now fully accessible via the [MarkLogic REST API](https://docs.marklogic.com/REST/client){:target="_blank"}
-
-A finished version of this tutorial is available here: [Finished Online Shopping Hub Example](https://github.com/marklogic/marklogic-data-hub/tree/develop/examples/online-store){:target="_blank"}
-
-## Uninstalling QuickStart
-
-You can uninstall QuickStart by clicking **Settings** in the top navigation and then **UNINSTALL HUB** on the page that appears.
-
-## Next Steps
-
-There are more resources available to help you on your MarkLogic Data Hub journey:
-
-- [MarkLogic University](https://mlu.marklogic.com/ondemand/index.xqy?q=Series%3A%22Operational%20Data%20Hubs%22): Data Hub Framework On Demand Video Courses
-- [Data Hub Framework Wiki](https://github.com/marklogic/marklogic-data-hub/wiki): Read about more advanced uses
-- [Data Hub Framework Examples](https://github.com/marklogic/marklogic-data-hub/tree/develop/examples): Many examples of how to use the Data Hub Framework
-
-Remember that QuickStart is a tool that helps show how the Data Hub Framework works. For production use, use MLCP or other tools for input flows and gradle tasks or your own code (using the provided [Java classes]({{site.baseurl}}/harmonize/java/)) for harmonize flows.
-
-Have a question? Use the [#marklogic-dhf tag on StackOverflow](https://stackoverflow.com/questions/ask?tags=marklogic-dhf).
-
-Have a comment? Found a bug? [File an issue on Github](https://github.com/marklogic/marklogic-data-hub/issues/new) and we will see it.
-
-
-{% include prev-next-nav-tut4xtoc.html gotopage="tutorial-toc.md" %}
diff --git a/_pages/tutorial/tutorial.md b/_pages/tutorial/tutorial.md
index 6476e0b16b..8b1e7bcaa7 100644
--- a/_pages/tutorial/tutorial.md
+++ b/_pages/tutorial/tutorial.md
@@ -6,7 +6,7 @@ permalink: /tutorial/
# Data Hub Framework Tutorial
-
+- [DHF 4.x Tutorial]({{site.baseurl}}/tutorial/4x/)
- [DHF 3.x Tutorial]({{site.baseurl}}/tutorial/3x/)
- [DHF 2.x Tutorial]({{site.baseurl}}/tutorial/2x/)
- [DHF 1.x Tutorial]({{site.baseurl}}/tutorial/1x/)
diff --git a/_source-images/qs-4x/Thumbs.db b/_source-images/qs-4x/Thumbs.db
new file mode 100644
index 0000000000..fc4f208579
Binary files /dev/null and b/_source-images/qs-4x/Thumbs.db differ
diff --git a/_source-images/qs-4x/qs-4x-browse-data-database-dropdown.snag b/_source-images/qs-4x/qs-4x-browse-data-database-dropdown.snag
new file mode 100644
index 0000000000..d3821373f7
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-browse-data-database-dropdown.snag differ
diff --git a/_source-images/qs-4x/qs-4x-browse-data-final.snag b/_source-images/qs-4x/qs-4x-browse-data-final.snag
new file mode 100644
index 0000000000..e706bd60e5
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-browse-data-final.snag differ
diff --git a/_source-images/qs-4x/qs-4x-browse-data-staging.snag b/_source-images/qs-4x/qs-4x-browse-data-staging.snag
new file mode 100644
index 0000000000..9460f9dfe7
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-browse-data-staging.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-add-properties-Customer.snag b/_source-images/qs-4x/qs-4x-entities-add-properties-Customer.snag
new file mode 100644
index 0000000000..a62429f75e
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-add-properties-Customer.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-add-properties-Order.snag b/_source-images/qs-4x/qs-4x-entities-add-properties-Order.snag
new file mode 100644
index 0000000000..e80953671a
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-add-properties-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-add-properties-Product.snag b/_source-images/qs-4x/qs-4x-entities-add-properties-Product.snag
new file mode 100644
index 0000000000..195a34756f
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-add-properties-Product.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-add-properties-cardinality-infinity-Order.snag b/_source-images/qs-4x/qs-4x-entities-add-properties-cardinality-infinity-Order.snag
new file mode 100644
index 0000000000..ca8c71181a
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-add-properties-cardinality-infinity-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-add-properties-type-entities-Order.snag b/_source-images/qs-4x/qs-4x-entities-add-properties-type-entities-Order.snag
new file mode 100644
index 0000000000..0683323f94
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-add-properties-type-entities-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card-vertical.snag b/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card-vertical.snag
new file mode 100644
index 0000000000..df7f5ff86a
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card-vertical.snag differ
diff --git a/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card.snag b/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card.snag
new file mode 100644
index 0000000000..ba99ac0180
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-entities-order-card-to-product-card.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Customer.snag b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Customer.snag
new file mode 100644
index 0000000000..9aa59a92a2
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Customer.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Order.snag b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Order.snag
new file mode 100644
index 0000000000..e580f25a99
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Product.snag b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Product.snag
new file mode 100644
index 0000000000..27744cb885
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-create-harmonize-flow-Product.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-create-complete.snag b/_source-images/qs-4x/qs-4x-flows-create-input-flow.snag
similarity index 100%
rename from _source-images/qs-4x/qs-4x-flows-create-complete.snag
rename to _source-images/qs-4x/qs-4x-flows-create-input-flow.snag
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-collector-custom-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-collector-custom-Order.snag
new file mode 100644
index 0000000000..f43a7f2a35
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-collector-custom-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-collector-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-collector-default-Order.snag
new file mode 100644
index 0000000000..3e7340320d
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-collector-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-content-custom-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-content-custom-Order.snag
new file mode 100644
index 0000000000..5407bfba33
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-content-custom-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-content-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-content-default-Order.snag
new file mode 100644
index 0000000000..09390d7d90
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-content-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-flow-info-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-flow-info-default-Order.snag
new file mode 100644
index 0000000000..db39a1da07
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-flow-info-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-headers-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-headers-default-Order.snag
new file mode 100644
index 0000000000..cf3acf1909
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-headers-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-main-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-main-default-Order.snag
new file mode 100644
index 0000000000..6cd865c7de
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-main-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-triples-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-triples-default-Order.snag
new file mode 100644
index 0000000000..f7422a0d87
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-triples-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-harmonize-writer-default-Order.snag b/_source-images/qs-4x/qs-4x-flows-harmonize-writer-default-Order.snag
new file mode 100644
index 0000000000..1fc485d488
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-harmonize-writer-default-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Customer.snag b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Customer.snag
new file mode 100644
index 0000000000..bc3a297bcf
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Customer.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Order.snag b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Order.snag
new file mode 100644
index 0000000000..c1d7b7a44a
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Order.snag differ
diff --git a/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Product.snag b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Product.snag
new file mode 100644
index 0000000000..5c99210e7e
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-flows-run-harmonize-flow-Product.snag differ
diff --git a/_source-images/qs-4x/qs-4x-jobs-postharmonize.snag b/_source-images/qs-4x/qs-4x-jobs-postharmonize.snag
new file mode 100644
index 0000000000..15beb84831
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-jobs-postharmonize.snag differ
diff --git a/_source-images/qs-4x/qs-4x-mappings-create-complete-Product.snag b/_source-images/qs-4x/qs-4x-mappings-create-complete-Product.snag
new file mode 100644
index 0000000000..4db25d2db5
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-mappings-create-complete-Product.snag differ
diff --git a/_source-images/qs-4x/qs-4x-mappings-create-complete.snag b/_source-images/qs-4x/qs-4x-mappings-create-complete.snag
new file mode 100644
index 0000000000..02c01ed336
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-mappings-create-complete.snag differ
diff --git a/_source-images/qs-4x/qs-4x-mappings-editor-Customer.snag b/_source-images/qs-4x/qs-4x-mappings-editor-Customer.snag
new file mode 100644
index 0000000000..76bc93d5af
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-mappings-editor-Customer.snag differ
diff --git a/_source-images/qs-4x/qs-4x-mappings-editor-Product.snag b/_source-images/qs-4x/qs-4x-mappings-editor-Product.snag
new file mode 100644
index 0000000000..de9887d026
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-mappings-editor-Product.snag differ
diff --git a/_source-images/qs-4x/qs-4x-settings.snag b/_source-images/qs-4x/qs-4x-settings.snag
new file mode 100644
index 0000000000..88f79cfb9a
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-settings.snag differ
diff --git a/_source-images/qs-4x/qs-4x-traces-postharmonize.snag b/_source-images/qs-4x/qs-4x-traces-postharmonize.snag
new file mode 100644
index 0000000000..0cd81c9b0d
Binary files /dev/null and b/_source-images/qs-4x/qs-4x-traces-postharmonize.snag differ
diff --git a/images/4x/qs-4x-browse-data-final.png b/images/4x/qs-4x-browse-data-final.png
new file mode 100644
index 0000000000..9dbb8910fd
Binary files /dev/null and b/images/4x/qs-4x-browse-data-final.png differ
diff --git a/images/4x/qs-4x-browse-data-staging.png b/images/4x/qs-4x-browse-data-staging.png
new file mode 100644
index 0000000000..3d359ce47d
Binary files /dev/null and b/images/4x/qs-4x-browse-data-staging.png differ
diff --git a/images/4x/qs-4x-entities-add-properties-Customer.png b/images/4x/qs-4x-entities-add-properties-Customer.png
new file mode 100644
index 0000000000..0e45ab2321
Binary files /dev/null and b/images/4x/qs-4x-entities-add-properties-Customer.png differ
diff --git a/images/4x/qs-4x-entities-add-properties-Order.png b/images/4x/qs-4x-entities-add-properties-Order.png
new file mode 100644
index 0000000000..a706dea794
Binary files /dev/null and b/images/4x/qs-4x-entities-add-properties-Order.png differ
diff --git a/images/4x/qs-4x-entities-add-properties-Product.png b/images/4x/qs-4x-entities-add-properties-Product.png
new file mode 100644
index 0000000000..88a7ea2b57
Binary files /dev/null and b/images/4x/qs-4x-entities-add-properties-Product.png differ
diff --git a/images/4x/qs-4x-entities-add-properties-type-entities-Order.png b/images/4x/qs-4x-entities-add-properties-type-entities-Order.png
new file mode 100644
index 0000000000..3abaf0ae60
Binary files /dev/null and b/images/4x/qs-4x-entities-add-properties-type-entities-Order.png differ
diff --git a/images/4x/qs-4x-entities-order-card-to-product-card.png b/images/4x/qs-4x-entities-order-card-to-product-card.png
new file mode 100644
index 0000000000..39a1b61c18
Binary files /dev/null and b/images/4x/qs-4x-entities-order-card-to-product-card.png differ
diff --git a/images/4x/qs-4x-entities-resize-card.png b/images/4x/qs-4x-entities-resize-card.png
index 42426b4e5d..4f7bce9b4d 100644
Binary files a/images/4x/qs-4x-entities-resize-card.png and b/images/4x/qs-4x-entities-resize-card.png differ
diff --git a/images/4x/qs-4x-flows-create-harmonize-flow-Customer.png b/images/4x/qs-4x-flows-create-harmonize-flow-Customer.png
new file mode 100644
index 0000000000..b4cda14424
Binary files /dev/null and b/images/4x/qs-4x-flows-create-harmonize-flow-Customer.png differ
diff --git a/images/4x/qs-4x-flows-create-harmonize-flow-Order.png b/images/4x/qs-4x-flows-create-harmonize-flow-Order.png
new file mode 100644
index 0000000000..99cc145915
Binary files /dev/null and b/images/4x/qs-4x-flows-create-harmonize-flow-Order.png differ
diff --git a/images/4x/qs-4x-flows-create-harmonize-flow-Product.png b/images/4x/qs-4x-flows-create-harmonize-flow-Product.png
new file mode 100644
index 0000000000..f0c7c443fa
Binary files /dev/null and b/images/4x/qs-4x-flows-create-harmonize-flow-Product.png differ
diff --git a/images/4x/qs-4x-flows-create-complete-Order.png b/images/4x/qs-4x-flows-create-input-flow-Order.png
similarity index 100%
rename from images/4x/qs-4x-flows-create-complete-Order.png
rename to images/4x/qs-4x-flows-create-input-flow-Order.png
diff --git a/images/4x/qs-4x-flows-create-complete-Product.png b/images/4x/qs-4x-flows-create-input-flow-Product.png
similarity index 100%
rename from images/4x/qs-4x-flows-create-complete-Product.png
rename to images/4x/qs-4x-flows-create-input-flow-Product.png
diff --git a/images/4x/qs-4x-flows-create-complete.png b/images/4x/qs-4x-flows-create-input-flow.png
similarity index 100%
rename from images/4x/qs-4x-flows-create-complete.png
rename to images/4x/qs-4x-flows-create-input-flow.png
diff --git a/images/4x/qs-4x-flows-harmonize-collector-custom-Order.png b/images/4x/qs-4x-flows-harmonize-collector-custom-Order.png
new file mode 100644
index 0000000000..a03e393c67
Binary files /dev/null and b/images/4x/qs-4x-flows-harmonize-collector-custom-Order.png differ
diff --git a/images/4x/qs-4x-flows-harmonize-content-custom-Order.png b/images/4x/qs-4x-flows-harmonize-content-custom-Order.png
new file mode 100644
index 0000000000..5188d3369a
Binary files /dev/null and b/images/4x/qs-4x-flows-harmonize-content-custom-Order.png differ
diff --git a/images/4x/qs-4x-flows-load-data-Product.png b/images/4x/qs-4x-flows-load-data-Product.png
index b9eb0ec78e..7ff120d0cb 100644
Binary files a/images/4x/qs-4x-flows-load-data-Product.png and b/images/4x/qs-4x-flows-load-data-Product.png differ
diff --git a/images/4x/qs-4x-flows-run-harmonize-flow-Customer.png b/images/4x/qs-4x-flows-run-harmonize-flow-Customer.png
new file mode 100644
index 0000000000..70f5d195f3
Binary files /dev/null and b/images/4x/qs-4x-flows-run-harmonize-flow-Customer.png differ
diff --git a/images/4x/qs-4x-flows-run-harmonize-flow-Order.png b/images/4x/qs-4x-flows-run-harmonize-flow-Order.png
new file mode 100644
index 0000000000..0fc7ec6abd
Binary files /dev/null and b/images/4x/qs-4x-flows-run-harmonize-flow-Order.png differ
diff --git a/images/4x/qs-4x-flows-run-harmonize-flow-Product.png b/images/4x/qs-4x-flows-run-harmonize-flow-Product.png
new file mode 100644
index 0000000000..890eb765c7
Binary files /dev/null and b/images/4x/qs-4x-flows-run-harmonize-flow-Product.png differ
diff --git a/images/4x/qs-4x-mappings-create-complete-Customer.png b/images/4x/qs-4x-mappings-create-complete-Customer.png
new file mode 100644
index 0000000000..37d19d22be
Binary files /dev/null and b/images/4x/qs-4x-mappings-create-complete-Customer.png differ
diff --git a/images/4x/qs-4x-mappings-create-complete-Product.png b/images/4x/qs-4x-mappings-create-complete-Product.png
new file mode 100644
index 0000000000..929ab3d4f8
Binary files /dev/null and b/images/4x/qs-4x-mappings-create-complete-Product.png differ
diff --git a/images/4x/qs-4x-mappings-editor-Customer.png b/images/4x/qs-4x-mappings-editor-Customer.png
new file mode 100644
index 0000000000..bc4280c3c6
Binary files /dev/null and b/images/4x/qs-4x-mappings-editor-Customer.png differ
diff --git a/images/4x/qs-4x-mappings-editor-Product.png b/images/4x/qs-4x-mappings-editor-Product.png
new file mode 100644
index 0000000000..acf590a7ae
Binary files /dev/null and b/images/4x/qs-4x-mappings-editor-Product.png differ
diff --git a/images/4x/qs-4x-settings-uninstall.png b/images/4x/qs-4x-settings-uninstall.png
new file mode 100644
index 0000000000..0e15e80fa3
Binary files /dev/null and b/images/4x/qs-4x-settings-uninstall.png differ