diff --git a/frontend/src/metabase/lib/query/field.js b/frontend/src/metabase/lib/query/field.js
index 266bfee8133a094c46561bd724000a983f87cc40..8fa7e367af1870553b1e689cadf05a94947a0a0d 100644
--- a/frontend/src/metabase/lib/query/field.js
+++ b/frontend/src/metabase/lib/query/field.js
@@ -3,6 +3,7 @@ import { mbqlEq } from "./util";
 
 import type { Field as FieldReference } from "metabase/meta/types/Query";
 import type { Field, FieldId, FieldValues } from "metabase/meta/types/Field";
+import type { Value } from "metabase/meta/types/Dataset";
 
 // gets the target field ID (recursively) from any type of field, including raw field ID, fk->, and datetime-field cast.
 export function getFieldTargetId(field: FieldReference): ?FieldId {
diff --git a/frontend/src/metabase/lib/schema_metadata.js b/frontend/src/metabase/lib/schema_metadata.js
index ca632802a2eb9f951e29c53810211ad122567ef5..cdcc1e330d0c40739ea8762ea01acc8989993ea3 100644
--- a/frontend/src/metabase/lib/schema_metadata.js
+++ b/frontend/src/metabase/lib/schema_metadata.js
@@ -1,7 +1,7 @@
 import _ from "underscore";
 
 import { isa, isFK as isTypeFK, isPK as isTypePK, TYPE } from "metabase/lib/types";
-import { getFieldValues, getHumanReadableValue } from "metabase/lib/query/field";
+import { getFieldValues } from "metabase/lib/query/field";
 
 // primary field types used for picking operators, etc
 export const NUMBER = "NUMBER";
@@ -182,7 +182,8 @@ function equivalentArgument(field, table) {
                     .filter(([value, displayValue]) => value != null)
                     .map(([value, displayValue]) => ({
                         key: value,
-                        name: getHumanReadableValue(value, values)
+                        // NOTE Atte Keinänen 8/7/17: Similar logic as in getHumanReadableValue of lib/query/field
+                        name: displayValue ? displayValue : String(value)
                     }))
                     .sort((a, b) => a.key === b.key ? 0 : (a.key < b.key ? -1 : 1))
             };
diff --git a/frontend/src/metabase/query_builder/containers/QueryBuilder.integ.spec.js b/frontend/src/metabase/query_builder/containers/QueryBuilder.integ.spec.js
index fbdebfbdc2c34726dc930ff59c06da0c9fa96560..0482002aba7bf83ec0f757b70b8af918470afb9d 100644
--- a/frontend/src/metabase/query_builder/containers/QueryBuilder.integ.spec.js
+++ b/frontend/src/metabase/query_builder/containers/QueryBuilder.integ.spec.js
@@ -25,7 +25,12 @@ import { SET_ERROR_PAGE } from "metabase/redux/app";
 
 import QueryHeader from "metabase/query_builder/components/QueryHeader";
 import { VisualizationEmptyState } from "metabase/query_builder/components/QueryVisualization";
-import { FETCH_TABLE_METADATA } from "metabase/redux/metadata";
+import {
+    deleteFieldDimension,
+    updateFieldDimension,
+    updateFieldValues,
+    FETCH_TABLE_METADATA
+} from "metabase/redux/metadata";
 import FieldList, { DimensionPicker } from "metabase/query_builder/components/FieldList";
 import FilterPopover from "metabase/query_builder/components/filters/FilterPopover";
 import VisualizationError from "metabase/query_builder/components/VisualizationError";
@@ -51,6 +56,10 @@ import ChartClickActions from "metabase/visualizations/components/ChartClickActi
 
 import { delay } from "metabase/lib/promise";
 
+const REVIEW_PRODUCT_ID = 32;
+const REVIEW_RATING_ID = 33;
+const PRODUCT_TITLE_ID = 27;
+
 const initQbWithDbAndTable = (dbId, tableId) => {
     return async () => {
         const store = await createTestStore()
@@ -108,7 +117,7 @@ describe("QueryBuilder", () => {
 
             expect(table.find('div[children="Created At"]').length).toBe(1);
 
-            const doneButton = settingsModal.find(".Button--primary.disabled")
+            const doneButton = settingsModal.find(".Button--primary")
             expect(doneButton.length).toBe(1)
 
             const fieldsToIncludeCheckboxes = settingsModal.find(CheckBox)
@@ -724,4 +733,124 @@ describe("QueryBuilder", () => {
             });
         })
     })
+
+    describe("remapping", () => {
+        beforeAll(async () => {
+            // add remappings
+            const store = await createTestStore()
+
+            // NOTE Atte Keinänen 8/7/17:
+            // We test here the full dimension functionality which lets you enter a dimension name that differs
+            // from the field name. This is something that field settings UI doesn't let you to do yet.
+
+            await store.dispatch(updateFieldDimension(REVIEW_PRODUCT_ID, {
+                type: "external",
+                name: "Product Name",
+                human_readable_field_id: PRODUCT_TITLE_ID
+            }));
+
+            await store.dispatch(updateFieldDimension(REVIEW_RATING_ID, {
+                type: "internal",
+                name: "Rating Description",
+                human_readable_field_id: null
+            }));
+            await store.dispatch(updateFieldValues(REVIEW_RATING_ID, [
+                [1, 'Awful'], [2, 'Unpleasant'], [3, 'Meh'], [4, 'Enjoyable'], [5, 'Perfecto']
+            ]));
+        })
+
+        describe("for Rating category field with custom field values", () => {
+            // The following test case is very similar to earlier filter tests but in this case we use remapped values
+            it("lets you add 'Rating is Perfecto' filter", async () => {
+                const { store, qb } = await initQBWithReviewsTable();
+
+                // open filter popover
+                const filterSection = qb.find('.GuiBuilder-filtered-by');
+                const newFilterButton = filterSection.find('.AddButton');
+                newFilterButton.simulate("click");
+
+                // choose the field to be filtered
+                const filterPopover = filterSection.find(FilterPopover);
+                const ratingFieldButton = filterPopover.find(FieldList).find('h4[children="Rating Description"]')
+                expect(ratingFieldButton.length).toBe(1);
+                ratingFieldButton.simulate('click');
+
+                // check that field values seem correct
+                const fieldItems = filterPopover.find('li');
+                expect(fieldItems.length).toBe(5);
+                expect(fieldItems.first().text()).toBe("Awful")
+                expect(fieldItems.last().text()).toBe("Perfecto")
+
+                // select the last item (Perfecto)
+                const widgetFieldItem = fieldItems.last();
+                const widgetCheckbox = widgetFieldItem.find(CheckBox);
+                expect(widgetCheckbox.props().checked).toBe(false);
+                widgetFieldItem.children().first().simulate("click");
+                expect(widgetCheckbox.props().checked).toBe(true);
+
+                // add the filter
+                const addFilterButton = filterPopover.find('button[children="Add filter"]')
+                addFilterButton.simulate("click");
+
+                await store.waitForActions([SET_DATASET_QUERY])
+                store.resetDispatchedActions();
+
+                // validate the filter text value
+                expect(qb.find(FilterPopover).length).toBe(0);
+                const filterWidget = qb.find(FilterWidget);
+                expect(filterWidget.length).toBe(1);
+                expect(filterWidget.text()).toBe("Rating Description is equal toPerfecto");
+            })
+
+            it("shows remapped value correctly in Raw Data query with Table visualization", async () => {
+                const { store, qb } = await initQBWithReviewsTable();
+
+                qb.find(RunButton).simulate("click");
+                await store.waitForActions([QUERY_COMPLETED]);
+
+                const table = qb.find(TestTable);
+                const headerCells = table.find("thead tr").first().find("th");
+                const firstRowCells = table.find("tbody tr").first().find("td");
+
+                expect(headerCells.length).toBe(6)
+                expect(headerCells.at(4).text()).toBe("Rating Description")
+
+                expect(firstRowCells.length).toBe(6);
+
+                expect(firstRowCells.at(4).text()).toBe("Enjoyable");
+            })
+        });
+
+        describe("for Product ID FK field with a FK remapping", () => {
+            it("shows remapped values correctly in Raw Data query with Table visualization", async () => {
+                const { store, qb } = await initQBWithReviewsTable();
+
+                qb.find(RunButton).simulate("click");
+                await store.waitForActions([QUERY_COMPLETED]);
+
+                const table = qb.find(TestTable);
+                const headerCells = table.find("thead tr").first().find("th");
+                const firstRowCells = table.find("tbody tr").first().find("td");
+
+                expect(headerCells.length).toBe(6)
+                expect(headerCells.at(3).text()).toBe("Product Name")
+
+                expect(firstRowCells.length).toBe(6);
+
+                expect(firstRowCells.at(3).text()).toBe("Ergonomic Leather Pants");
+            })
+        });
+
+        afterAll(async () => {
+            const store = await createTestStore()
+
+            await store.dispatch(deleteFieldDimension(REVIEW_PRODUCT_ID));
+            await store.dispatch(deleteFieldDimension(REVIEW_RATING_ID));
+
+            await store.dispatch(updateFieldValues(REVIEW_RATING_ID, [
+                [1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5']
+            ]));
+        })
+
+    })
 });
diff --git a/frontend/src/metabase/visualizations/components/TableSimple.jsx b/frontend/src/metabase/visualizations/components/TableSimple.jsx
index c8ac3e8f94375ab35ae15aeca098b63ada92649d..2e68740a628bc06cb363e2ac9dc979c00a474f98 100644
--- a/frontend/src/metabase/visualizations/components/TableSimple.jsx
+++ b/frontend/src/metabase/visualizations/components/TableSimple.jsx
@@ -9,8 +9,7 @@ import ExplicitSize from "metabase/components/ExplicitSize.jsx";
 import Ellipsified from "metabase/components/Ellipsified.jsx";
 import Icon from "metabase/components/Icon.jsx";
 
-import { formatValue } from "metabase/lib/formatting";
-import { getFriendlyName } from "metabase/visualizations/lib/utils";
+import { formatColumn, formatValue } from "metabase/lib/formatting";
 import { getTableCellClickedObject, isColumnRightAligned } from "metabase/visualizations/lib/table";
 
 import cx from "classnames";
@@ -112,7 +111,7 @@ export default class TableSimple extends Component {
                                                     width={8} height={8}
                                                     style={{ position: "absolute", right: "100%", marginRight: 3 }}
                                                 />
-                                                <Ellipsified>{getFriendlyName(col)}</Ellipsified>
+                                                <Ellipsified>{formatColumn(col)}</Ellipsified>
                                             </div>
                                         </th>
                                     )}
diff --git a/frontend/src/metabase/visualizations/lib/utils.js b/frontend/src/metabase/visualizations/lib/utils.js
index 66316013ff08710efa172628184a631149b26079..a06b85ec50eb1a5b12e778034326760fa8156a18 100644
--- a/frontend/src/metabase/visualizations/lib/utils.js
+++ b/frontend/src/metabase/visualizations/lib/utils.js
@@ -135,7 +135,14 @@ export function getXValues(datas, chartType) {
 }
 
 export function getFriendlyName(column) {
-    return column.display_name || FRIENDLY_NAME_MAP[column.name.toLowerCase().trim()] || column.name;
+    if (column.display_name && column.display_name !== column.name) {
+        return column.display_name
+    } else {
+        // NOTE Atte Keinänen 8/7/17:
+        // Values `display_name` and `name` are same for breakout columns so check FRIENDLY_NAME_MAP
+        // before returning either `display_name` or `name`
+        return FRIENDLY_NAME_MAP[column.name.toLowerCase().trim()] || column.display_name || column.name;
+    }
 }
 
 export function getCardColors(card) {