Skip to content

Commit e1d16a7

Browse files
Merge #279
279: Adds support to multi-index search r=brunoocasali a=ahmednfwela # Pull Request ## Related issue Related to #267 ## What does this PR do? - Introduce `SearchQuery`, `MultiSearchQuery`, `MultiSearchResult` - Add `Future<MultiSearchResult> multiSearch(MultiSearchQuery requests);` to `MeiliSearchClient` - [Breaking Change] Changed the signature of `Searcheable`, `SearchResult`, `PaginatedSearchResult` to be immutable with prober inheritance ## PR checklist Please check if your PR fulfills the following requirements: - [ ] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)? - [x] Have you read the contributing guidelines? - [x] Have you made sure that the title is accurate and descriptive of the changes? Thank you so much for contributing to Meilisearch! cc `@brunoocasali` after his holiday :) Co-authored-by: Ahmed Fwela <ahmednfwela@bdaya-dev.com>
2 parents 45058e2 + 6f171fa commit e1d16a7

11 files changed

+266
-56
lines changed

lib/meilisearch.dart

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ export 'src/client.dart';
44
export 'src/index.dart';
55
export 'src/settings/_exports.dart';
66
export 'src/search_result.dart';
7+
export 'src/multi_search_result.dart';
8+
export 'src/multi_search_query.dart';
9+
export 'src/search_query.dart';
710
export 'src/filter_builder/_exports.dart';
811
export 'src/paginated_search_result.dart';
912
export 'src/query_parameters/documents_query.dart';

lib/src/client.dart

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:meilisearch/src/swap_index.dart';
1010
import 'package:meilisearch/src/tasks_results.dart';
1111
import 'package:meilisearch/src/task.dart';
1212

13+
import 'multi_search_query.dart';
14+
import 'multi_search_result.dart';
1315
import 'http_request.dart';
1416
import 'index.dart';
1517
import 'client_impl.dart';
@@ -51,6 +53,9 @@ abstract class MeiliSearchClient {
5153
DateTime? expiresAt,
5254
});
5355

56+
/// does a Multi-index search
57+
Future<MultiSearchResult> multiSearch(MultiSearchQuery requests);
58+
5459
/// Create an index object by given [uid].
5560
MeiliSearchIndex index(String uid);
5661

lib/src/client_impl.dart

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:meilisearch/src/tasks_results.dart';
1010
import 'package:meilisearch/src/task.dart';
1111
import 'package:meilisearch/src/tenant_token.dart';
1212

13+
import 'multi_search_result.dart';
14+
import 'multi_search_query.dart';
1315
import 'http_request.dart';
1416
import 'http_request_impl.dart';
1517

@@ -258,4 +260,14 @@ class MeiliSearchClientImpl implements MeiliSearchClient {
258260

259261
return Task.fromMap(response.data!);
260262
}
263+
264+
@override
265+
Future<MultiSearchResult> multiSearch(MultiSearchQuery query) async {
266+
final response = await http.postMethod<Map<String, Object?>>(
267+
'/multi-search',
268+
data: query.toMap(),
269+
);
270+
271+
return MultiSearchResult.fromMap(response.data!);
272+
}
261273
}

lib/src/index_impl.dart

+28-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dart:convert';
22
import 'package:dio/dio.dart';
33
import 'result.dart';
4+
import 'search_query.dart';
45
import 'searchable.dart';
56
import 'settings/_exports.dart';
67
import 'tasks_results.dart';
@@ -126,30 +127,33 @@ class MeiliSearchIndexImpl implements MeiliSearchIndex {
126127
String? highlightPostTag,
127128
MatchingStrategy? matchingStrategy,
128129
}) async {
129-
final data = <String, Object?>{
130-
'q': query,
131-
'offset': offset,
132-
'limit': limit,
133-
'page': page,
134-
'hitsPerPage': hitsPerPage,
135-
'filter': filter ?? filterExpression?.transform(),
136-
'sort': sort,
137-
'facets': facets,
138-
'attributesToRetrieve': attributesToRetrieve,
139-
'attributesToCrop': attributesToCrop,
140-
'cropLength': cropLength,
141-
'attributesToHighlight': attributesToHighlight,
142-
'showMatchesPosition': showMatchesPosition,
143-
'cropMarker': cropMarker,
144-
'highlightPreTag': highlightPreTag,
145-
'highlightPostTag': highlightPostTag,
146-
'matchingStrategy': matchingStrategy?.name,
147-
};
148-
data.removeWhere((k, v) => v == null);
149-
final response = await http
150-
.postMethod<Map<String, Object?>>('/indexes/$uid/search', data: data);
151-
152-
return Searcheable.createSearchResult(response.data!);
130+
final data = SearchQuery(
131+
indexUid: uid,
132+
query: query,
133+
offset: offset,
134+
limit: limit,
135+
page: page,
136+
hitsPerPage: hitsPerPage,
137+
filter: filter,
138+
filterExpression: filterExpression,
139+
sort: sort,
140+
facets: facets,
141+
attributesToRetrieve: attributesToRetrieve,
142+
attributesToCrop: attributesToCrop,
143+
cropLength: cropLength,
144+
attributesToHighlight: attributesToHighlight,
145+
showMatchesPosition: showMatchesPosition,
146+
cropMarker: cropMarker,
147+
highlightPreTag: highlightPreTag,
148+
highlightPostTag: highlightPostTag,
149+
matchingStrategy: matchingStrategy,
150+
).toMap();
151+
152+
final response = await http.postMethod<Map<String, Object?>>(
153+
'/indexes/$uid/search',
154+
data: data..remove('indexUid'));
155+
156+
return Searcheable.createSearchResult(response.data!, indexUid: uid);
153157
}
154158

155159
//

lib/src/multi_search_query.dart

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import 'search_query.dart';
2+
3+
class MultiSearchQuery {
4+
final List<SearchQuery> queries;
5+
6+
const MultiSearchQuery({
7+
required this.queries,
8+
});
9+
10+
Map<String, Object?> toMap() {
11+
return {'queries': queries.map((e) => e.toMap()).toList()};
12+
}
13+
}

lib/src/multi_search_result.dart

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'searchable.dart';
2+
3+
class MultiSearchResult {
4+
final List<Searcheable> results;
5+
6+
const MultiSearchResult({
7+
required this.results,
8+
});
9+
10+
factory MultiSearchResult.fromMap(Map<String, Object?> json) {
11+
final list = json['results'] as List<Object?>;
12+
final parsed = list
13+
.cast<Map<String, Object?>>()
14+
.map((e) => Searcheable.createSearchResult(e));
15+
16+
return MultiSearchResult(results: parsed.toList());
17+
}
18+
}

lib/src/paginated_search_result.dart

+24-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
import 'package:meilisearch/src/searchable.dart';
22

33
class PaginatedSearchResult extends Searcheable {
4-
PaginatedSearchResult({
4+
const PaginatedSearchResult({
5+
List<Map<String, Object?>>? hits,
6+
Object? facetDistribution,
7+
Object? matchesPosition,
8+
int? processingTimeMs,
9+
String? query,
10+
required String indexUid,
511
this.hitsPerPage,
612
this.page,
713
this.totalHits,
814
this.totalPages,
9-
});
15+
}) : super(
16+
facetDistribution: facetDistribution,
17+
hits: hits,
18+
matchesPosition: matchesPosition,
19+
processingTimeMs: processingTimeMs,
20+
query: query,
21+
indexUid: indexUid,
22+
);
1023

1124
/// Number of documents skipped
1225
final int? hitsPerPage;
@@ -20,20 +33,19 @@ class PaginatedSearchResult extends Searcheable {
2033
/// Total number of pages
2134
final int? totalPages;
2235

23-
factory PaginatedSearchResult.fromMap(Map<String, Object?> map) {
24-
var result = PaginatedSearchResult(
36+
factory PaginatedSearchResult.fromMap(Map<String, Object?> map,
37+
{String? indexUid}) {
38+
return PaginatedSearchResult(
2539
page: map['page'] as int?,
2640
hitsPerPage: map['hitsPerPage'] as int?,
2741
totalHits: map['totalHits'] as int?,
2842
totalPages: map['totalPages'] as int?,
43+
hits: (map['hits'] as List?)?.cast<Map<String, Object?>>(),
44+
query: map['query'] as String?,
45+
processingTimeMs: map['processingTimeMs'] as int?,
46+
facetDistribution: map['facetDistribution'],
47+
matchesPosition: map['_matchesPosition'],
48+
indexUid: indexUid ?? map['indexUid'] as String,
2949
);
30-
31-
result.hits = (map['hits'] as List?)?.cast<Map<String, Object?>>();
32-
result.query = map['query'] as String?;
33-
result.processingTimeMs = map['processingTimeMs'] as int?;
34-
result.facetDistribution = map['facetDistribution'];
35-
result.matchesPosition = map['_matchesPosition'];
36-
37-
return result;
3850
}
3951
}

lib/src/search_query.dart

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import 'filter_builder/_exports.dart';
2+
import 'matching_strategy_enum.dart';
3+
4+
class SearchQuery {
5+
final String indexUid;
6+
final String? query;
7+
final int? offset;
8+
final int? limit;
9+
final int? page;
10+
final int? hitsPerPage;
11+
final Object? filter;
12+
final MeiliOperatorExpressionBase? filterExpression;
13+
final List<String>? sort;
14+
final List<String>? facets;
15+
final List<String>? attributesToRetrieve;
16+
final List<String>? attributesToCrop;
17+
final int? cropLength;
18+
final List<String>? attributesToHighlight;
19+
final bool? showMatchesPosition;
20+
final String? cropMarker;
21+
final String? highlightPreTag;
22+
final String? highlightPostTag;
23+
final MatchingStrategy? matchingStrategy;
24+
25+
const SearchQuery({
26+
required this.query,
27+
required this.indexUid,
28+
this.offset,
29+
this.limit,
30+
this.page,
31+
this.hitsPerPage,
32+
this.filter,
33+
this.filterExpression,
34+
this.sort,
35+
this.facets,
36+
this.attributesToRetrieve,
37+
this.attributesToCrop,
38+
this.cropLength,
39+
this.attributesToHighlight,
40+
this.showMatchesPosition,
41+
this.cropMarker,
42+
this.highlightPreTag,
43+
this.highlightPostTag,
44+
this.matchingStrategy,
45+
});
46+
47+
Map<String, Object> toMap() {
48+
return (<String, Object?>{
49+
'indexUid': indexUid,
50+
'q': query,
51+
'offset': offset,
52+
'limit': limit,
53+
'page': page,
54+
'hitsPerPage': hitsPerPage,
55+
'filter': filter ?? filterExpression?.transform(),
56+
'sort': sort,
57+
'facets': facets,
58+
'attributesToRetrieve': attributesToRetrieve,
59+
'attributesToCrop': attributesToCrop,
60+
'cropLength': cropLength,
61+
'attributesToHighlight': attributesToHighlight,
62+
'showMatchesPosition': showMatchesPosition,
63+
'cropMarker': cropMarker,
64+
'highlightPreTag': highlightPreTag,
65+
'highlightPostTag': highlightPostTag,
66+
'matchingStrategy': matchingStrategy?.name,
67+
}..removeWhere((key, value) => value == null))
68+
.cast<String, Object>();
69+
}
70+
}

lib/src/search_result.dart

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import 'package:meilisearch/src/searchable.dart';
22

33
class SearchResult extends Searcheable {
4-
SearchResult({
4+
const SearchResult({
5+
List<Map<String, Object?>>? hits,
6+
Object? facetDistribution,
7+
Object? matchesPosition,
8+
int? processingTimeMs,
9+
String? query,
10+
required String indexUid,
511
this.offset,
612
this.limit,
713
this.estimatedTotalHits,
8-
});
14+
}) : super(
15+
facetDistribution: facetDistribution,
16+
hits: hits,
17+
matchesPosition: matchesPosition,
18+
processingTimeMs: processingTimeMs,
19+
query: query,
20+
indexUid: indexUid,
21+
);
922

1023
/// Number of documents skipped
1124
final int? offset;
@@ -16,19 +29,17 @@ class SearchResult extends Searcheable {
1629
/// Estimated number of matches
1730
final int? estimatedTotalHits;
1831

19-
factory SearchResult.fromMap(Map<String, Object?> map) {
20-
var result = SearchResult(
32+
factory SearchResult.fromMap(Map<String, Object?> map, {String? indexUid}) {
33+
return SearchResult(
2134
limit: map['limit'] as int?,
2235
offset: map['offset'] as int?,
2336
estimatedTotalHits: map['estimatedTotalHits'] as int?,
37+
hits: (map['hits'] as List?)?.cast<Map<String, Object?>>(),
38+
query: map['query'] as String?,
39+
processingTimeMs: map['processingTimeMs'] as int?,
40+
facetDistribution: map['facetDistribution'],
41+
matchesPosition: map['_matchesPosition'],
42+
indexUid: indexUid ?? map['indexUid'] as String,
2443
);
25-
26-
result.hits = (map['hits'] as List?)?.cast<Map<String, Object?>>();
27-
result.query = map['query'] as String?;
28-
result.processingTimeMs = map['processingTimeMs'] as int?;
29-
result.facetDistribution = map['facetDistribution'];
30-
result.matchesPosition = map['_matchesPosition'];
31-
32-
return result;
3344
}
3445
}

lib/src/searchable.dart

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
import 'package:meilisearch/meilisearch.dart';
22

33
abstract class Searcheable {
4+
final String indexUid;
5+
46
/// Query originating the response
5-
String? query;
7+
final String? query;
68

79
/// Results of the query
8-
List<Map<String, Object?>>? hits;
10+
final List<Map<String, Object?>>? hits;
911

1012
/// Distribution of the given facets
11-
Object? facetDistribution;
13+
final Object? facetDistribution;
1214

1315
/// Contains the location of each occurrence of queried terms across all fields
14-
Object? matchesPosition;
16+
final Object? matchesPosition;
1517

1618
/// Processing time of the query
17-
int? processingTimeMs;
19+
final int? processingTimeMs;
20+
21+
const Searcheable({
22+
required this.indexUid,
23+
this.query,
24+
this.hits,
25+
this.facetDistribution,
26+
this.matchesPosition,
27+
this.processingTimeMs,
28+
});
1829

19-
static Searcheable createSearchResult(Map<String, Object?> map) {
30+
static Searcheable createSearchResult(Map<String, Object?> map,
31+
{String? indexUid}) {
2032
if (map['totalHits'] != null) {
21-
return PaginatedSearchResult.fromMap(map);
33+
return PaginatedSearchResult.fromMap(map, indexUid: indexUid);
2234
} else {
23-
return SearchResult.fromMap(map);
35+
return SearchResult.fromMap(map, indexUid: indexUid);
2436
}
2537
}
2638
}

0 commit comments

Comments
 (0)