Skip to content

Commit bd8ea7d

Browse files
MV88offtherailz
andauthored
Fix 3976 Search Plugin improved (#3977)
* Fix 3976 Search Plugin improved * sort is correct * maxRsults is configurable and limits the results size * add test to check results sorting * Update web/client/epics/search.js Co-Authored-By: Lorenzo Natali <offtherailz@gmail.com> * Update web/client/epics/__tests__/search-test.js Co-Authored-By: Lorenzo Natali <offtherailz@gmail.com>
1 parent 193cb08 commit bd8ea7d

9 files changed

Lines changed: 2329 additions & 35 deletions

File tree

web/client/actions/__tests__/search-test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ var {
3333
describe('Test correctness of the search actions', () => {
3434

3535
it('text search started', () => {
36-
const action = textSearch(true);
36+
const action = textSearch("via Milano", {}, 25);
3737
expect(action.type).toBe(TEXT_SEARCH_STARTED);
38+
expect(action.searchText).toBe("via Milano");
39+
expect(action.maxResults).toBe(25);
3840
});
3941
it('text search loading', () => {
4042
const action = searchTextLoading(true);

web/client/actions/search.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@ function addMarker(itemPosition, itemText) {
148148
* perform a text search
149149
* @memberof actions.search
150150
* @param {string} searchText the text to search
151-
* @param {object} options [{}] the search options. Contain the services
151+
* @param {object} [options={}] - the search options. Contain the services
152+
* @param {number} [maxResults=15] - the max results obtained from all the services
152153
*/
153-
function textSearch(searchText, {services = null} = {}) {
154+
function textSearch(searchText, {services = null} = {}, maxResults = 15) {
154155
return {
155156
type: TEXT_SEARCH_STARTED,
156157
searchText,
157-
services
158+
services,
159+
maxResults
158160
};
159161
}
160162

web/client/components/mapcontrols/search/SearchBar.jsx

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,47 +74,48 @@ require('./searchbar.css');
7474
*/
7575
class SearchBar extends React.Component {
7676
static propTypes = {
77+
autoFocusOnSelect: PropTypes.bool,
78+
blurResetDelay: PropTypes.number,
7779
className: PropTypes.string,
80+
delay: PropTypes.number,
81+
error: PropTypes.object,
82+
hideOnBlur: PropTypes.bool,
83+
isSearchClickable: PropTypes.bool,
84+
loading: PropTypes.bool,
85+
maxResults: PropTypes.number,
86+
onCancelSelectedItem: PropTypes.func,
87+
onPurgeResults: PropTypes.func,
7888
onSearch: PropTypes.func,
7989
onSearchReset: PropTypes.func,
80-
onPurgeResults: PropTypes.func,
8190
onSearchTextChange: PropTypes.func,
82-
onCancelSelectedItem: PropTypes.func,
91+
optionsIcon: PropTypes.string,
8392
placeholder: PropTypes.string,
8493
placeholderMsgId: PropTypes.string,
85-
delay: PropTypes.number,
86-
hideOnBlur: PropTypes.bool,
87-
blurResetDelay: PropTypes.number,
88-
typeAhead: PropTypes.bool,
89-
searchText: PropTypes.string,
9094
removeIcon: PropTypes.string,
91-
optionsIcon: PropTypes.string,
9295
searchIcon: PropTypes.string,
96+
searchOptions: PropTypes.object,
97+
searchText: PropTypes.string,
9398
selectedItems: PropTypes.array,
94-
autoFocusOnSelect: PropTypes.bool,
9599
splitTools: PropTypes.bool,
96-
isSearchClickable: PropTypes.bool,
97-
loading: PropTypes.bool,
98-
error: PropTypes.object,
99100
style: PropTypes.object,
100-
searchOptions: PropTypes.object,
101+
typeAhead: PropTypes.bool,
101102
// menuOptions
103+
activeSearchTool: PropTypes.string,
104+
aeronauticalOptions: PropTypes.object,
105+
constraintsCoordEditor: PropTypes.object,
106+
coordinate: PropTypes.object,
107+
defaultZoomLevel: PropTypes.number,
108+
enabledSearchServicesConfig: PropTypes.bool,
109+
format: PropTypes.string,
102110
onChangeActiveSearchTool: PropTypes.func,
103-
onChangeFormat: PropTypes.func,
104111
onChangeCoord: PropTypes.func,
112+
onChangeFormat: PropTypes.func,
105113
onClearCoordinatesSearch: PropTypes.func,
106114
onToggleControl: PropTypes.func,
107115
onZoomToPoint: PropTypes.func,
108-
format: PropTypes.string,
109-
activeSearchTool: PropTypes.string,
110-
defaultZoomLevel: PropTypes.number,
111-
showOptions: PropTypes.bool,
112116
showAddressSearchOption: PropTypes.bool,
113117
showCoordinatesSearchOption: PropTypes.bool,
114-
enabledSearchServicesConfig: PropTypes.bool,
115-
aeronauticalOptions: PropTypes.object,
116-
constraintsCoordEditor: PropTypes.object,
117-
coordinate: PropTypes.object
118+
showOptions: PropTypes.bool
118119
};
119120

120121
static contextTypes = {
@@ -137,6 +138,7 @@ class SearchBar extends React.Component {
137138
autoFocusOnSelect: true,
138139
splitTools: true,
139140
isSearchClickable: true,
141+
maxResults: 15,
140142
typeAhead: true,
141143
searchText: "",
142144
hideOnBlur: true,
@@ -491,7 +493,7 @@ class SearchBar extends React.Component {
491493
if ((text === undefined || text === "") && (!this.props.selectedItems || this.props.selectedItems.length === 0)) {
492494
this.props.onSearchReset();
493495
} else if (text !== undefined && text !== "") {
494-
this.props.onSearch(text, this.props.searchOptions);
496+
this.props.onSearch(text, this.props.searchOptions, this.props.maxResults);
495497
}
496498
};
497499
zoomToPoint = () => {

web/client/components/mapcontrols/search/__tests__/SearchBar-test.jsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,18 @@ describe("test the SearchBar", () => {
160160
let searchOptions = {displaycrs: "EPSG:3857"};
161161

162162
const renderSearchBar = (testHandlers, text) => {
163-
return ReactDOM.render(<SearchBar searchOptions={searchOptions} searchText={text} delay={0} typeAhead={false} onSearch={testHandlers.onSearchHandler} onSearchReset={testHandlers.onSearchResetHandler} onSearchTextChange={testHandlers.onSearchTextChangeHandler}/>, document.getElementById("container"));
163+
return ReactDOM.render(
164+
<SearchBar
165+
searchOptions={searchOptions}
166+
searchText={text}
167+
delay={0}
168+
maxResults={23}
169+
typeAhead={false}
170+
onSearch={testHandlers.onSearchHandler}
171+
onSearchReset={testHandlers.onSearchResetHandler}
172+
onSearchTextChange={testHandlers.onSearchTextChangeHandler}
173+
/>, document.getElementById("container")
174+
);
164175
};
165176

166177
const testHandlers = {
@@ -177,7 +188,7 @@ describe("test the SearchBar", () => {
177188
TestUtils.Simulate.change(input);
178189
TestUtils.Simulate.keyDown(input, {key: "Enter", keyCode: 13, which: 13});
179190
expect(spy.calls.length).toEqual(1);
180-
expect(spy).toHaveBeenCalledWith('test', searchOptions);
191+
expect(spy).toHaveBeenCalledWith('test', searchOptions, 23);
181192
});
182193
it('test error and loading status', () => {
183194
const tb = ReactDOM.render(<SearchBar loading error={{message: "TEST_ERROR"}}/>, document.getElementById("container"));

web/client/epics/__tests__/search-test.js

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
* LICENSE file in the root directory of this source tree.
88
*/
99

10-
var expect = require('expect');
10+
const expect = require('expect');
11+
const {head, last} = require('lodash');
1112

1213
const configureMockStore = require('redux-mock-store').default;
1314
const { createEpicMiddleware, combineEpics } = require('redux-observable');
@@ -34,7 +35,7 @@ const SEARCH_NESTED = 'SEARCH NESTED';
3435
const TEST_NESTED_PLACEHOLDER = 'TEST_NESTED_PLACEHOLDER';
3536
const STATE_NAME = 'STATE_NAME';
3637

37-
const {testEpic} = require('./epicTestUtils');
38+
const {testEpic, addTimeoutEpic} = require('./epicTestUtils');
3839

3940
const nestedService = {
4041
nestedPlaceholder: TEST_NESTED_PLACEHOLDER
@@ -98,6 +99,7 @@ describe('search Epics', () => {
9899
}
99100
});
100101
});
102+
101103
it('produces the selectSearchItem epic', () => {
102104
let action = selectSearchItem({
103105
"type": "Feature",
@@ -246,4 +248,81 @@ describe('search Epics', () => {
246248

247249
});
248250
});
251+
it('check the search result resorting is conservative and the number of results limited to maxResults', (done) => {
252+
const maxResults = 5;
253+
let action = {
254+
...textSearch("TEST"),
255+
services: [{
256+
type: 'wfs',
257+
options: {
258+
url: 'base/web/client/test-resources/wfs/Wyoming_18_results.json',
259+
typeName: 'topp:states',
260+
queriableAttributes: [STATE_NAME],
261+
returnFullData: false
262+
}
263+
}],
264+
maxResults
265+
};
266+
267+
const NUMBER_OF_ACTIONS = 4;
268+
testEpic(
269+
addTimeoutEpic(searchEpic, 100),
270+
NUMBER_OF_ACTIONS,
271+
[action],
272+
actions => {
273+
expect(actions.length).toBe(NUMBER_OF_ACTIONS);
274+
expect(actions[1].type).toBe(TEXT_SEARCH_LOADING);
275+
expect(actions[2].type).toBe(TEXT_SEARCH_RESULTS_LOADED);
276+
expect(actions[2].results.length).toBe(maxResults);
277+
expect(head(actions[2].results).id).toBe("states.1");
278+
expect(last(actions[2].results).id).toBe("states.5");
279+
expect(actions[3].type).toBe(TEXT_SEARCH_LOADING);
280+
done();
281+
}, {});
282+
});
283+
it('produces the search epic with two services with more results than default limit', (done) => {
284+
const maxResults = 5;
285+
let action = {
286+
...textSearch("TEST"),
287+
services: [{
288+
type: 'wfs',
289+
options: {
290+
url: 'base/web/client/test-resources/wfs/Wyoming_18_results.json',
291+
typeName: 'topp:states',
292+
queriableAttributes: [STATE_NAME],
293+
returnFullData: false
294+
}
295+
},
296+
{
297+
type: 'wfs',
298+
options: {
299+
url: 'base/web/client/test-resources/wfs/Arizona_18_results.json',
300+
typeName: 'topp:states',
301+
queriableAttributes: [STATE_NAME],
302+
returnFullData: false
303+
}
304+
}],
305+
maxResults
306+
};
307+
308+
const NUMBER_OF_ACTIONS = 5;
309+
testEpic(
310+
addTimeoutEpic(searchEpic, 100),
311+
NUMBER_OF_ACTIONS,
312+
[action],
313+
actions => {
314+
expect(actions.length).toBe(NUMBER_OF_ACTIONS);
315+
expect(actions[1].type).toBe(TEXT_SEARCH_LOADING);
316+
expect(actions[2].type).toBe(TEXT_SEARCH_RESULTS_LOADED);
317+
expect(actions[2].results.length).toBe(maxResults);
318+
expect(head(actions[2].results).id).toBe("states.1");
319+
expect(last(actions[2].results).id).toBe("states.5");
320+
expect(actions[3].type).toBe(TEXT_SEARCH_RESULTS_LOADED);
321+
expect(actions[3].results.length).toBe(maxResults);
322+
expect(head(actions[3].results).id).toBe("states.1");
323+
expect(last(actions[3].results).id).toBe("states.5");
324+
expect(actions[4].type).toBe(TEXT_SEARCH_LOADING);
325+
done();
326+
}, {});
327+
});
249328
});

web/client/epics/search.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const toBbox = require('turf-bbox');
3737
const {generateTemplateString} = require('../utils/TemplateUtils');
3838
const assign = require('object-assign');
3939

40-
const {get} = require('lodash');
40+
const {sortBy} = require('lodash');
4141

4242
/**
4343
* Gets every `TEXT_SEARCH_STARTED` event.
@@ -47,7 +47,7 @@ const {get} = require('lodash');
4747
* @memberof epics.search
4848
* @return {external:Observable}
4949
*/
50-
const searchEpic = action$ =>
50+
const searchEpic = (action$) =>
5151
action$.ofType(TEXT_SEARCH_STARTED)
5252
.debounceTime(250)
5353
.switchMap( action =>
@@ -77,8 +77,9 @@ const searchEpic = action$ =>
7777
)// from
7878
// merge all results from the streams
7979
.mergeAll()
80-
.scan( (oldRes, newRes) => [...oldRes, ...newRes].sort( (a, b) => get(b, "__PRIORITY__") - get(a, "__PRIORITY__") ) .slice(0, 15))
81-
.map((results) => searchResultLoaded(results, false))
80+
.scan( (oldRes, newRes) => sortBy([...oldRes, ...newRes], ["__PRIORITY__"]))
81+
// limit the number of results returned from all services to maxResults
82+
.map((results) => searchResultLoaded(results.slice(0, action.maxResults || 15), false))
8283
.startWith(searchTextLoading(true))
8384
.takeUntil(action$.ofType( TEXT_SEARCH_RESULTS_PURGE, TEXT_SEARCH_RESET, TEXT_SEARCH_ITEM_SELECTED))
8485
.concat([searchTextLoading(false)])

web/client/plugins/Search.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ const ToggleButton = require('./searchbar/ToggleButton');
125125
* @class Search
126126
* @memberof plugins
127127
* @prop {object} cfg.searchOptions initial search options
128+
* @prop {object} cfg.maxResults number of max items present in the result list
128129
* @prop {object} cfg.resultsStyle custom style for search results
129130
* @prop {bool} cfg.fitResultsToMapSize true by default, fits the result list to the mapSize (can be disabled, for custom uses)
130131
* @prop {searchService[]} cfg.searchOptions.services a list of services to perform search.

0 commit comments

Comments
 (0)