From 0d35c5ed595b3029287fb498c894438747a16172 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 10 Sep 2017 12:53:59 -0400 Subject: [PATCH] Give preset priority in preset list when search matches name exactly (closes #4325) --- modules/presets/collection.js | 14 ++++++++-- test/spec/presets/collection.js | 49 +++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/modules/presets/collection.js b/modules/presets/collection.js index 35e2b023b7..e4b48939f0 100644 --- a/modules/presets/collection.js +++ b/modules/presets/collection.js @@ -56,13 +56,23 @@ export function presetCollection(collection) { var leading_name = _.filter(searchable, function(a) { return leading(a.name().toLowerCase()); }).sort(function(a, b) { - var i; + var aCompare = a.name().toLowerCase(), + bCompare = b.name().toLowerCase(), + i; + + // priority if search string matches preset name exactly - #4325 + if (value === aCompare) return -1; + if (value === bCompare) return 1; + + // priority for higher matchScore i = b.originalScore - a.originalScore; if (i !== 0) return i; - i = a.name().toLowerCase().indexOf(value) - b.name().toLowerCase().indexOf(value); + // priority if search string appears earlier in preset name + i = aCompare.indexOf(value) - bCompare.indexOf(value); if (i !== 0) return i; + // priority for shorter preset names return a.name().length - b.name().length; }); diff --git a/test/spec/presets/collection.js b/test/spec/presets/collection.js index e88912329c..45af5e4c3a 100644 --- a/test/spec/presets/collection.js +++ b/test/spec/presets/collection.js @@ -49,7 +49,14 @@ describe('iD.presetCollection', function() { name: 'Park', tags: { leisure: 'park' }, geometry: ['point', 'area'], - terms: [ 'grass' ] + terms: [ 'grass' ], + matchScore: 0.5 + }), + parking: iD.presetPreset('__test/amenity/parking', { + name: 'Parking', + tags: { amenity: 'parking' }, + geometry: ['point', 'area'], + terms: [ 'cars' ] }), soccer: iD.presetPreset('__test/leisure/pitch/soccer', { name: 'Soccer Field', @@ -68,7 +75,7 @@ describe('iD.presetCollection', function() { var c = iD.presetCollection([ p.point, p.line, p.area, p.grill, p.sandpit, p.residential, - p.grass1, p.grass2, p.park, p.soccer, p.football + p.grass1, p.grass2, p.park, p.parking, p.soccer, p.football ]); describe('#item', function() { @@ -93,30 +100,42 @@ describe('iD.presetCollection', function() { it('returns alternate matches in correct order', function() { var col = c.search('gri', 'point').matchGeometry('point').collection; - expect(col.indexOf(p.grill)).to.eql(0); // 1. 'Grill' (leading name) - expect(col.indexOf(p.football)).to.eql(7); // 2. 'Football' (leading term 'gridiron') - expect(col.indexOf(p.sandpit)).to.eql(1); // 3. 'Sandpit' (leading tag value 'grit_bin') - expect(col.indexOf(p.grass1)).to.be.within(2,3); // 4. 'Grass' (similar name) - expect(col.indexOf(p.grass2)).to.be.within(3,4); // 5. 'Ğṝȁß' (similar name) - expect(col.indexOf(p.park)).to.eql(4); // 6. 'Park' (similar term 'grass') + expect(col.indexOf(p.grill), 'Grill').to.eql(0); // 1. 'Grill' (leading name) + expect(col.indexOf(p.football), 'Football').to.eql(1); // 2. 'Football' (leading term 'gridiron') + expect(col.indexOf(p.sandpit), 'Sandpit').to.eql(2); // 3. 'Sandpit' (leading tag value 'grit_bin') + expect(col.indexOf(p.grass1), 'Grass').to.be.within(3,4); // 4. 'Grass' (similar name) + expect(col.indexOf(p.grass2), 'Ğṝȁß').to.be.within(3,4); // 5. 'Ğṝȁß' (similar name) + expect(col.indexOf(p.park), 'Park').to.eql(5); // 6. 'Park' (similar term 'grass') + }); + + it('sorts preset with matchScore penalty below others', function() { + var col = c.search('par', 'point').matchGeometry('point').collection; + expect(col.indexOf(p.parking), 'Parking').to.eql(0); // 1. 'Parking' (default matchScore) + expect(col.indexOf(p.park), 'Park').to.eql(1); // 2. 'Park' (low matchScore) + }); + + it('ignores matchScore penalty for exact name match', function() { + var col = c.search('park', 'point').matchGeometry('point').collection; + expect(col.indexOf(p.park), 'Park').to.eql(0); // 1. 'Park' (low matchScore) + expect(col.indexOf(p.parking), 'Parking').to.eql(1); // 2. 'Parking' (default matchScore) }); it('considers diacritics on exact matches', function() { var col = c.search('ğṝȁ', 'point').matchGeometry('point').collection; - expect(col.indexOf(p.grass2)).to.eql(0); // 1. 'Ğṝȁß' (leading name) - expect(col.indexOf(p.grass1)).to.eql(1); // 2. 'Grass' (similar name) + expect(col.indexOf(p.grass2), 'Ğṝȁß').to.eql(0); // 1. 'Ğṝȁß' (leading name) + expect(col.indexOf(p.grass1), 'Grass').to.eql(1); // 2. 'Grass' (similar name) }); it('replaces diacritics on fuzzy matches', function() { var col = c.search('graß', 'point').matchGeometry('point').collection; - expect(col.indexOf(p.grass1)).to.be.within(0,1); // 1. 'Grass' (similar name) - expect(col.indexOf(p.grass2)).to.be.within(0,1); // 2. 'Ğṝȁß' (similar name) + expect(col.indexOf(p.grass1), 'Grass').to.be.within(0,1); // 1. 'Grass' (similar name) + expect(col.indexOf(p.grass2), 'Ğṝȁß').to.be.within(0,1); // 2. 'Ğṝȁß' (similar name) }); it('includes the appropriate fallback preset', function() { - expect(c.search('foo', 'point').collection).to.include(p.point); - expect(c.search('foo', 'line').collection).to.include(p.line); - expect(c.search('foo', 'area').collection).to.include(p.area); + expect(c.search('foo', 'point').collection, 'point').to.include(p.point); + expect(c.search('foo', 'line').collection, 'line').to.include(p.line); + expect(c.search('foo', 'area').collection, 'area').to.include(p.area); }); it('excludes presets with searchable: false', function() {