Improve suggestion combo behavior in the name field

- adds minItems(1) so it will match
- displays up to 10 suggestions
- correctly removes old tags by setting the `undefined`
- adjust the name distance scoring
- only shows name suggestions if the preset can benefit from them
  (i.e. a generic/fallback or a preset that has some suggestions in the index)
This commit is contained in:
Bryan Housel
2018-11-21 11:15:03 -05:00
parent b1a2e30b22
commit 622621ca89
3 changed files with 93 additions and 57 deletions
+25 -30
View File
@@ -9,8 +9,8 @@ import { utilEditDistance } from '../util/index';
export function presetCollection(collection) {
var maxSearchResults = 50,
maxSuggestionResults = 10;
var maxSearchResults = 50;
var maxSuggestionResults = 10;
var presets = {
@@ -51,20 +51,20 @@ export function presetCollection(collection) {
value = value.toLowerCase();
var searchable = _filter(this.collection, function(a) {
return a.searchable !== false && a.suggestion !== true;
}),
suggestions = _filter(this.collection, function(a) {
return a.suggestion === true;
});
return a.searchable !== false && a.suggestion !== true;
});
var suggestions = _filter(this.collection, function(a) {
return a.suggestion === true;
});
// matches value to preset.name
var leading_name = _filter(searchable, function(a) {
return leading(a.name().toLowerCase());
}).sort(function(a, b) {
var aCompare = a.name().toLowerCase(),
bCompare = b.name().toLowerCase(),
i;
var aCompare = a.name().toLowerCase();
var bCompare = b.name().toLowerCase();
var i;
// priority if search string matches preset name exactly - #4325
if (value === aCompare) return -1;
@@ -84,21 +84,19 @@ export function presetCollection(collection) {
// matches value to preset.terms values
var leading_terms = _filter(searchable, function(a) {
return _some(a.terms() || [], leading);
});
return _some(a.terms() || [], leading);
});
// matches value to preset.tags values
var leading_tag_values = _filter(searchable, function(a) {
return _some(_without(_values(a.tags || {}), '*'), leading);
});
return _some(_without(_values(a.tags || {}), '*'), leading);
});
// finds close matches to value in preset.name
var similar_name = searchable.map(function(a) {
return {
preset: a,
dist: utilEditDistance(value, a.name())
};
var similar_name = searchable
.map(function(a) {
return { preset: a, dist: utilEditDistance(value, a.name()) };
}).filter(function(a) {
return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3;
}).sort(function(a, b) {
@@ -125,10 +123,7 @@ export function presetCollection(collection) {
});
var similar_suggestions = suggestions.map(function(a) {
return {
preset: a,
dist: utilEditDistance(value, suggestionName(a.name()))
};
return { preset: a, dist: utilEditDistance(value, suggestionName(a.name())) };
}).filter(function(a) {
return a.dist + Math.min(value.length - suggestionName(a.preset.name()).length, 0) < 1;
}).sort(function(a, b) {
@@ -140,13 +135,13 @@ export function presetCollection(collection) {
var other = presets.item(geometry);
var results = leading_name.concat(
leading_terms,
leading_tag_values,
leading_suggestions.slice(0, maxSuggestionResults + 5),
similar_name,
similar_terms,
similar_suggestions.slice(0, maxSuggestionResults)
).slice(0, maxSearchResults - 1);
leading_terms,
leading_tag_values,
leading_suggestions.slice(0, maxSuggestionResults + 5),
similar_name,
similar_terms,
similar_suggestions.slice(0, maxSuggestionResults)
).slice(0, maxSearchResults - 1);
return presetCollection(_uniq(results.concat(other)));
}
+2 -2
View File
@@ -28,8 +28,8 @@ export function presetPreset(id, preset, fields) {
preset.matchScore = function(entity) {
var tags = preset.tags,
score = 0;
var tags = preset.tags;
var score = 0;
for (var t in tags) {
if (entity.tags[t] === tags[t]) {
+66 -25
View File
@@ -46,12 +46,48 @@ export function uiFieldLocalized(field, context) {
.merge(input);
if (field.id === 'name') {
// var preset = context.presets().match(_entity, context.graph());
// input
// .call(d3_combobox()
// .container(context.container())
// .fetcher(suggestNames(preset, dataSuggestions))
// );
var presets = context.presets();
var preset = presets.match(_entity, context.graph());
var isFallback = preset.isFallback();
var pTag = preset.id.split('/', 2);
var pKey = pTag[0];
var pValue = pTag[1];
var allSuggestions = presets.collection.filter(function(p) {
return p.suggestion === true;
});
// This code attempts to determine if the matched preset is the
// kind of preset that even can benefit from name suggestions..
// - true = shops, cafes, hotels, etc. (also generic and fallback presets)
// - false = churches, parks, hospitals, etc. (things not in the index)
var goodSuggestions = allSuggestions.filter(function(s) {
if (isFallback) return true;
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
return pKey === sKey && (!pValue || pValue === sValue);
});
// Show the suggestions.. If the user picks one, change the tags..
if (allSuggestions.length && goodSuggestions.length) {
input
.call(d3_combobox()
.container(context.container())
.fetcher(suggestNames(preset, allSuggestions))
.minItems(1)
.on('accept', function(d) {
var tags = _entity.tags;
var geometry = _entity.geometry(context.graph());
var removed = preset.removeTags(tags, geometry);
for (var k in tags) {
tags[k] = removed[k]; // set removed tags to `undefined`
}
tags = d.suggestion.applyTags(tags, geometry);
dispatch.call('change', this, tags);
})
);
}
}
input
@@ -87,31 +123,36 @@ export function uiFieldLocalized(field, context) {
function suggestNames(preset, suggestions) {
preset = preset.id.split('/', 2);
var k = preset[0];
var v = preset[1];
var pTag = preset.id.split('/', 2);
var pKey = pTag[0];
var pValue = pTag[1];
return function(value, callback) {
var result = [];
var results = [];
if (value && value.length > 2) {
if (suggestions[k] && suggestions[k][v]) {
for (var sugg in suggestions[k][v]) {
var dist = utilEditDistance(value, sugg.substring(0, value.length));
if (dist < 3) {
result.push({
title: sugg,
value: sugg,
dist: dist
});
}
for (var i = 0; i < suggestions.length; i++) {
var s = suggestions[i];
var sTag = s.id.split('/', 2);
var sKey = sTag[0];
var sValue = sTag[1];
var name = s.name();
var dist = utilEditDistance(value, name.substring(0, value.length));
var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
if (dist < 1 || (matchesPreset && dist < 3)) {
var obj = {
title: name,
value: name,
suggestion: s,
dist: dist + (matchesPreset ? 0 : 1) // penalize if not matched preset
};
results.push(obj);
}
}
result.sort(function(a, b) {
return a.dist - b.dist;
});
results.sort(function(a, b) { return a.dist - b.dist; });
}
result = result.slice(0,3);
callback(result);
results = results.slice(0, 10);
callback(results);
};
}