Skip to content

Commit b473bc8

Browse files
committed
Try to use cache.
1 parent 3a885f6 commit b473bc8

File tree

5 files changed

+206
-16
lines changed

5 files changed

+206
-16
lines changed

lib/CustomWebSearchPage.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class _CustomWebSearchDemoState extends State<CustomWebSearchDemo> {
5151
),
5252
actions: <Widget>[
5353
new IconButton(
54-
tooltip: 'Image Search',
54+
tooltip: 'Search',
5555
icon: const Icon(Icons.search),
5656
onPressed: triggerShowSearch,
5757
),

lib/search_data_source.dart

Lines changed: 194 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:flutter/services.dart' show rootBundle;
88
import "package:googleapis_auth/auth_io.dart" as auth;
99
import 'package:googleapis/customsearch/v1.dart' as customsearch;
1010
import 'package:english_words/english_words.dart';
11+
import 'package:async/async.dart';
1112

1213
/// A wrapper class for [customsearch.Result].
1314
/// [SearchResult] will use the landing page link to measure if two results are
@@ -50,9 +51,6 @@ class SearchResult {
5051
: result.image.contextLink.hashCode;
5152
}
5253

53-
/// A wrapper class to generate search request. To make caching possible.
54-
class SearchRequest {}
55-
5654
class Promotion {
5755
final customsearch.Promotion promotion;
5856

@@ -76,8 +74,8 @@ class SearchResults {
7674

7775
SearchResults(customsearch.Search search) {
7876
var results = new List<SearchResult>();
79-
search.items.forEach((item) =>
80-
results.add(SearchResult.escapeLineBreakInSnippet(item)));
77+
search.items.forEach(
78+
(item) => results.add(SearchResult.escapeLineBreakInSnippet(item)));
8179
// Deduplicate search result.
8280
this.searchResults = Set<SearchResult>.from(results).toList();
8381
}
@@ -107,6 +105,9 @@ class FakeSearchDataSource implements SearchDataSource {
107105
'promotion': _StaticSearchResponse(
108106
assetPath: 'res/sampledata/nytimes_with_promotion.json'),
109107
};
108+
final ExpireCache<SearchQuery, SearchResults> _cache =
109+
ExpireCache<SearchQuery, SearchResults>();
110+
final String cx = 'fake_cx';
110111

111112
FakeSearchDataSource() {
112113
searchResponses.keys.forEach((key) {
@@ -119,6 +120,8 @@ class FakeSearchDataSource implements SearchDataSource {
119120
await rootBundle.loadString(assetPath);
120121
}
121122

123+
int count = 0;
124+
122125
@override
123126
Future<SearchResults> search(String query, {String searchType}) async {
124127
if (!searchResponses.containsKey(query)) {
@@ -127,18 +130,186 @@ class FakeSearchDataSource implements SearchDataSource {
127130
if (searchResponses[query].searchType != searchType) {
128131
return SearchResults.empty();
129132
}
133+
SearchQuery searchQuery =
134+
SearchQuery(query, this.cx, searchType: searchType);
135+
var cachedResponse = await _cache.get(searchQuery);
136+
if (cachedResponse != null) {
137+
return cachedResponse;
138+
}
130139
Map searchMap = jsonDecode(searchResponses[query].searchResponseJsonString);
131140
customsearch.Search search = customsearch.Search.fromJson(searchMap);
132-
return SearchResults(search);
141+
142+
print('count: $count');
143+
count++;
144+
145+
var result = SearchResults(search);
146+
await _cache.set(searchQuery, result);
147+
return result;
133148
}
134149
}
135150

151+
/// A wrapper class for search request, to make caching search request possible.
152+
class SearchQuery {
153+
String q;
154+
String c2coff;
155+
String cr;
156+
String cx;
157+
String dateRestrict;
158+
String exactTerms;
159+
String excludeTerms;
160+
String fileType;
161+
String filter;
162+
String gl;
163+
String googlehost;
164+
String highRange;
165+
String hl;
166+
String hq;
167+
String imgColorType;
168+
String imgDominantColor;
169+
String imgSize;
170+
String imgType;
171+
String linkSite;
172+
String lowRange;
173+
String lr;
174+
int num;
175+
String orTerms;
176+
String relatedSite;
177+
String rights;
178+
String safe;
179+
String searchType;
180+
String siteSearch;
181+
String siteSearchFilter;
182+
String sort;
183+
int start;
184+
185+
/// Used to get partial response, see:
186+
/// https://developers.google.com/custom-search/v1/performance#partial
187+
String fields;
188+
189+
SearchQuery(this.q, this.cx,
190+
{this.c2coff,
191+
this.cr,
192+
this.dateRestrict,
193+
this.exactTerms,
194+
this.excludeTerms,
195+
this.fileType,
196+
this.filter,
197+
this.gl,
198+
this.googlehost,
199+
this.highRange,
200+
this.hl,
201+
this.hq,
202+
this.imgColorType,
203+
this.imgDominantColor,
204+
this.imgSize,
205+
this.imgType,
206+
this.linkSite,
207+
this.lowRange,
208+
this.lr,
209+
this.num,
210+
this.orTerms,
211+
this.relatedSite,
212+
this.rights,
213+
this.safe,
214+
this.searchType,
215+
this.siteSearch,
216+
this.siteSearchFilter,
217+
this.sort,
218+
this.start,
219+
this.fields});
220+
221+
Future<SearchResults> runSearch(customsearch.CustomsearchApi api) async {
222+
return SearchResults(
223+
await api.cse.list(q, cx: cx, searchType: this.searchType));
224+
}
225+
226+
@override
227+
String toString() {
228+
return 'SearchQuery{q: $q, c2coff: $c2coff, cr: $cr, cx: $cx, dateRestrict: $dateRestrict, exactTerms: $exactTerms, excludeTerms: $excludeTerms, fileType: $fileType, filter: $filter, gl: $gl, googlehost: $googlehost, highRange: $highRange, hl: $hl, hq: $hq, imgColorType: $imgColorType, imgDominantColor: $imgDominantColor, imgSize: $imgSize, imgType: $imgType, linkSite: $linkSite, lowRange: $lowRange, lr: $lr, num: $num, orTerms: $orTerms, relatedSite: $relatedSite, rights: $rights, safe: $safe, searchType: $searchType, siteSearch: $siteSearch, siteSearchFilter: $siteSearchFilter, sort: $sort, start: $start, fields: $fields}';
229+
}
230+
231+
@override
232+
bool operator ==(Object other) =>
233+
identical(this, other) ||
234+
other is SearchQuery &&
235+
runtimeType == other.runtimeType &&
236+
q == other.q &&
237+
c2coff == other.c2coff &&
238+
cr == other.cr &&
239+
cx == other.cx &&
240+
dateRestrict == other.dateRestrict &&
241+
exactTerms == other.exactTerms &&
242+
excludeTerms == other.excludeTerms &&
243+
fileType == other.fileType &&
244+
filter == other.filter &&
245+
gl == other.gl &&
246+
googlehost == other.googlehost &&
247+
highRange == other.highRange &&
248+
hl == other.hl &&
249+
hq == other.hq &&
250+
imgColorType == other.imgColorType &&
251+
imgDominantColor == other.imgDominantColor &&
252+
imgSize == other.imgSize &&
253+
imgType == other.imgType &&
254+
linkSite == other.linkSite &&
255+
lowRange == other.lowRange &&
256+
lr == other.lr &&
257+
num == other.num &&
258+
orTerms == other.orTerms &&
259+
relatedSite == other.relatedSite &&
260+
rights == other.rights &&
261+
safe == other.safe &&
262+
searchType == other.searchType &&
263+
siteSearch == other.siteSearch &&
264+
siteSearchFilter == other.siteSearchFilter &&
265+
sort == other.sort &&
266+
start == other.start &&
267+
fields == other.fields;
268+
269+
@override
270+
int get hashCode =>
271+
q.hashCode ^
272+
c2coff.hashCode ^
273+
cr.hashCode ^
274+
cx.hashCode ^
275+
dateRestrict.hashCode ^
276+
exactTerms.hashCode ^
277+
excludeTerms.hashCode ^
278+
fileType.hashCode ^
279+
filter.hashCode ^
280+
gl.hashCode ^
281+
googlehost.hashCode ^
282+
highRange.hashCode ^
283+
hl.hashCode ^
284+
hq.hashCode ^
285+
imgColorType.hashCode ^
286+
imgDominantColor.hashCode ^
287+
imgSize.hashCode ^
288+
imgType.hashCode ^
289+
linkSite.hashCode ^
290+
lowRange.hashCode ^
291+
lr.hashCode ^
292+
num.hashCode ^
293+
orTerms.hashCode ^
294+
relatedSite.hashCode ^
295+
rights.hashCode ^
296+
safe.hashCode ^
297+
searchType.hashCode ^
298+
siteSearch.hashCode ^
299+
siteSearchFilter.hashCode ^
300+
sort.hashCode ^
301+
start.hashCode ^
302+
fields.hashCode;
303+
}
304+
136305
/// The search data source that uses Custom Search API.
137306
class CustomSearchDataSource implements SearchDataSource {
138307
final String cx;
139308
final String apiKey;
140-
var api;
309+
customsearch.CustomsearchApi api;
141310
int searchCount = 0;
311+
final ExpireCache<SearchQuery, SearchResults> _cache =
312+
ExpireCache<SearchQuery, SearchResults>();
142313

143314
CustomSearchDataSource({@required this.cx, @required this.apiKey}) {
144315
var client = auth.clientViaApiKey(apiKey);
@@ -150,9 +321,20 @@ class CustomSearchDataSource implements SearchDataSource {
150321
if (query.isEmpty) {
151322
return SearchResults.empty();
152323
}
153-
customsearch.Search search =
154-
await this.api.cse.list(query, cx: this.cx, searchType: searchType);
155-
return SearchResults(search);
324+
SearchQuery searchQuery =
325+
SearchQuery(query, this.cx, searchType: searchType);
326+
327+
final cachedResponse = await _cache.get(searchQuery);
328+
if (cachedResponse != null) {
329+
return cachedResponse;
330+
}
331+
332+
print('count: $searchCount');
333+
searchCount++;
334+
335+
final result = await searchQuery.runSearch(this.api);
336+
await _cache.set(searchQuery, result);
337+
return result;
156338
}
157339
}
158340

@@ -162,6 +344,8 @@ abstract class AutoCompleteDataSource {
162344

163345
class CommonEnglishWordAutoCompleteDataSource
164346
implements AutoCompleteDataSource {
347+
const CommonEnglishWordAutoCompleteDataSource();
348+
165349
@override
166350
List<String> getAutoCompletions({String query, int resultNumber = 10}) {
167351
var results = all.where((String word) => word.startsWith(query)).toList();

lib/search_result_page.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,33 @@ import 'shared_constant.dart';
88

99
class CustomSearchSearchDelegate extends SearchDelegate<SearchResult> {
1010
SearchDataSource dataSource;
11-
AutoCompleteDataSource autoCompleteDataSource =
12-
CommonEnglishWordAutoCompleteDataSource();
11+
AutoCompleteDataSource autoCompleteDataSource;
1312
SearchType searchType;
1413

1514
CustomSearchSearchDelegate(
1615
{this.dataSource,
17-
this.autoCompleteDataSource,
16+
this.autoCompleteDataSource =
17+
const CommonEnglishWordAutoCompleteDataSource(),
1818
this.searchType = SearchType.web});
1919

2020
CustomSearchSearchDelegate.imageSearch(
2121
{this.dataSource,
22-
this.autoCompleteDataSource,
22+
this.autoCompleteDataSource =
23+
const CommonEnglishWordAutoCompleteDataSource(),
2324
this.searchType = SearchType.image});
2425

2526
CustomSearchSearchDelegate.fakeStaticSource() {
2627
this.dataSource = FakeSearchDataSource();
2728
this.searchType = SearchType.web;
29+
this.autoCompleteDataSource =
30+
const CommonEnglishWordAutoCompleteDataSource();
2831
}
2932

3033
CustomSearchSearchDelegate.fakeStaticSourceImageSearch() {
3134
this.dataSource = FakeSearchDataSource();
3235
this.searchType = SearchType.image;
36+
this.autoCompleteDataSource =
37+
const CommonEnglishWordAutoCompleteDataSource();
3338
}
3439

3540
@override
@@ -74,6 +79,7 @@ class CustomSearchSearchDelegate extends SearchDelegate<SearchResult> {
7479
tooltip: 'Clear',
7580
icon: const Icon(Icons.clear),
7681
onPressed: () async {
82+
close(context, null);
7783
await showSearch<SearchResult>(
7884
context: context,
7985
delegate: this,

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies:
2727
googleapis_auth: ^0.2.6
2828
english_words: ^3.1.5
2929
url_launcher: ^4.0.3
30-
expire_cache: ^1.0.3
30+
expire_cache: ^1.0.4
3131

3232
dev_dependencies:
3333
test: ^1.5.1+1

res/imgs/examples/20190124.gif

25.5 MB
Loading

0 commit comments

Comments
 (0)