Skip to content

Commit fd1d0e5

Browse files
committed
feat(assignLabels): improve duplicate label handling
1 parent 8d72ac1 commit fd1d0e5

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

middleware/assignLabels.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const _ = require('lodash');
22

3-
const defaultLabelGenerator = require('pelias-labels');
3+
const defaultLabelGenerator = require('pelias-labels').partsGenerator;
44

55
function setup(labelGenerator) {
66
function middleware(req, res, next) {
@@ -10,17 +10,66 @@ function setup(labelGenerator) {
1010
return middleware;
1111
}
1212

13+
function getLabelFromLayer(labelParts, layer) {
14+
const part = labelParts.find(p => p.layer === layer);
15+
return _.get(part, 'label');
16+
}
17+
18+
function filterUnambiguousParts(second) {
19+
return (labelParts) => {
20+
if (labelParts.role === 'required') {
21+
return false;
22+
}
23+
const label = getLabelFromLayer(second.labelParts, labelParts.layer);
24+
return label && label !== labelParts.label;
25+
};
26+
}
27+
28+
function getBestLayers(results) {
29+
const bestLayers = new Set();
30+
const first = results[0];
31+
// Ensure deduplication based on optional elements even when the first two elements are equals.
32+
for (let i = 1; i < results.length; i++) {
33+
const second = results[i];
34+
first.labelParts.filter(filterUnambiguousParts(second)).map((p) => bestLayers.add(p.layer));
35+
if (bestLayers.size > 0) {
36+
// For now, we break as soon as we find a discriminant.
37+
break;
38+
}
39+
}
40+
return bestLayers;
41+
}
42+
1343
function assignLabel(req, res, next, labelGenerator) {
1444

1545
// do nothing if there's nothing to process
1646
if (!res || !res.data) {
1747
return next();
1848
}
1949

50+
// This object will help for label deduplication
51+
const dedupLabel = {};
52+
53+
// First we assign for all result the default label with all required layers
2054
res.data.forEach(function (result) {
21-
result.label = labelGenerator(result, _.get(req, 'clean.lang.iso6393'));
55+
const { labelParts, separator } = labelGenerator(result, _.get(req, 'clean.lang.iso6393'));
56+
result.label = labelParts.filter(e => e.role === 'required').map(e => e.label).join(separator);
57+
dedupLabel[result.label] = dedupLabel[result.label] || [];
58+
dedupLabel[result.label].push({ result, labelParts, separator });
2259
});
2360

61+
// We check all values with more than one entry
62+
Object.values(dedupLabel)
63+
.filter(results => results.length > 1)
64+
.forEach(results => {
65+
// This array will contain all optional layers that should be displayed
66+
const bestLayers = getBestLayers(results);
67+
// We reassign the label with the new value
68+
results.forEach(({ result, labelParts, separator }) => {
69+
result.label = labelParts.filter(e => e.role === 'required' || bestLayers.has(e.layer)).map(e => e.label).join(separator);
70+
});
71+
});
72+
2473
next();
2574
}
2675

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"morgan": "^1.8.2",
5151
"pelias-compare": "^0.1.16",
5252
"pelias-config": "^5.0.1",
53-
"pelias-labels": "^1.19.0",
53+
"pelias-labels": "pelias/labels#joxit/feat/with-optional",
5454
"pelias-logger": "^1.2.0",
5555
"pelias-microservice-wrapper": "^1.10.0",
5656
"pelias-model": "^9.0.0",

test/unit/middleware/assignLabels.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
var proxyquire = require('proxyquire').noCallThru();
22

3+
function partsGenerator(cb) {
4+
return { partsGenerator: cb };
5+
}
6+
37
module.exports.tests = {};
48

59
module.exports.tests.serialization = function(test, common) {
@@ -30,10 +34,10 @@ module.exports.tests.serialization = function(test, common) {
3034
test('labels should be assigned to all results', function(t) {
3135
var labelGenerator = function(result) {
3236
if (result.id === 1) {
33-
return 'label 1';
37+
return { labelParts: [{ label: 'label 1', role: 'required' }], separator: ', '};
3438
}
3539
if (result.id === 2) {
36-
return 'label 2';
40+
return { labelParts: [{ label: 'label 2', role: 'required' }], separator: ', '};
3741
}
3842

3943
};
@@ -73,11 +77,11 @@ module.exports.tests.serialization = function(test, common) {
7377

7478
test('no explicit labelGenerator supplied should use pelias-labels module', function(t) {
7579
var assignLabels = proxyquire('../../../middleware/assignLabels', {
76-
'pelias-labels': function(result) {
80+
'pelias-labels': partsGenerator(function(result) {
7781
if (result.id === 1) {
78-
return 'label 1';
82+
return { labelParts: [{ label: 'label 1', role: 'required' }], separator: ', '};
7983
}
80-
}
84+
})
8185
})();
8286

8387
var input = {

0 commit comments

Comments
 (0)