Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Result container #350

Merged
merged 14 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# the documentation on build
# You can read more on https://github.com/meilisearch/documentation/tree/master/.vuepress/code-samples
---
search_parameter_guide_show_ranking_score_1: |-
await client.index('movies').search('dragon', SearchQuery(showRankingScore: true));
facet_search_2: |-
await client.index('books').updateFaceting(Faceting(sortFacetValuesBy: {'genres': 'count'}));
getting_started_faceting: |-
Expand Down
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ First of all, thank you for contributing to Meilisearch! The goal of this docume

Install the official [Dart SDK](https://dart.dev/get-dart) or the [Flutter SDK](https://flutter.dev/docs/get-started/install) (which includes Dart SDK) using guides on the official website.

Both of them include `pub`. But if you want to run the linter you need to install the Flutter SDK.
Both of them include `pub`.

### Setup <!-- omit in TOC -->

Expand All @@ -54,13 +54,13 @@ docker-compose run --rm package bash -c "dart pub get && dart run test --concurr
To install dependencies:

```bash
pub get
dart pub get
```

Or if you are using Flutter SDK:
This package relies on [build_runner](https://pub.dev/packages/build_runner) to generate serialization information for some models, to re-generate files after making any changes, run:

```bash
flutter pub get
dart run build_runner build
```

### Tests and Linter <!-- omit in TOC -->
Expand All @@ -71,9 +71,9 @@ Each PR should pass the tests and the linter to be accepted.
# Tests
curl -L https://install.meilisearch.com | sh # download Meilisearch
./meilisearch --master-key=masterKey --no-analytics # run Meilisearch
pub run test --concurrency=4
dart test
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dart test by default uses concurrency

# Linter
flutter analyze
dart analyze
```

## Git Guidelines
Expand Down
29 changes: 29 additions & 0 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,35 @@
return Task.fromMap(response.data!);
}

/// Get the status of all experimental features that can be toggled at runtime
@RequiredMeiliServerVersion('1.3.0')

Check warning on line 129 in lib/src/client.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/client.dart#L129

Added line #L129 was not covered by tests
Future<ExperimentalFeatures> getExperimentalFeatures() async {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did not provide these kinds of methods to the other SDKs. I understand they are very convenient during development but we believe they are not useful after that, since you only enable/disable once :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can mark them as internal as a warning to users to not use this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok to keep, my concern is because it is more code to maintain and it will be present only in this SDK :)

final response = await http.getMethod<Map<String, Object?>>(

Check warning on line 131 in lib/src/client.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/client.dart#L131

Added line #L131 was not covered by tests
'/experimental-features',
);
return ExperimentalFeatures.fromJson(response.data!);

Check warning on line 134 in lib/src/client.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/client.dart#L134

Added line #L134 was not covered by tests
}

/// Set the status of experimental features that can be toggled at runtime
@RequiredMeiliServerVersion('1.3.0')
Future<ExperimentalFeatures> updateExperimentalFeatures(
UpdateExperimentalFeatures input,
) async {
final inputJson = input.toJson();
if (inputJson.isEmpty) {
throw ArgumentError.value(

Check warning on line 144 in lib/src/client.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/client.dart#L144

Added line #L144 was not covered by tests
input,
'input',
'input must contain at least one entry',
);
}
final response = await http.patchMethod<Map<String, Object?>>(
'/experimental-features',
data: input.toJson(),
);
return ExperimentalFeatures.fromJson(response.data!);
}

/// Return health of the Meilisearch server.
/// Throws an error if containing details if Meilisearch can't process your request.
Future<Map<String, dynamic>> health() async {
Expand Down
10 changes: 10 additions & 0 deletions lib/src/query_parameters/index_search_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
super.highlightPostTag,
super.matchingStrategy,
super.attributesToSearchOn,
super.showRankingScore,
super.vector,
super.showRankingScoreDetails,
});

@override
Expand Down Expand Up @@ -62,6 +65,9 @@
String? highlightPostTag,
MatchingStrategy? matchingStrategy,
List<String>? attributesToSearchOn,
bool? showRankingScore,
List<dynamic /* double | List<double> */ >? vector,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wish dart had type unions 😢

bool? showRankingScoreDetails,
}) =>
IndexSearchQuery(
query: query ?? this.query,
Expand All @@ -85,5 +91,9 @@
highlightPostTag: highlightPostTag ?? this.highlightPostTag,
matchingStrategy: matchingStrategy ?? this.matchingStrategy,
attributesToSearchOn: attributesToSearchOn ?? this.attributesToSearchOn,
showRankingScore: showRankingScore ?? this.showRankingScore,
vector: vector ?? this.vector,

Check warning on line 95 in lib/src/query_parameters/index_search_query.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/query_parameters/index_search_query.dart#L94-L95

Added lines #L94 - L95 were not covered by tests
showRankingScoreDetails:
showRankingScoreDetails ?? this.showRankingScoreDetails,

Check warning on line 97 in lib/src/query_parameters/index_search_query.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/query_parameters/index_search_query.dart#L97

Added line #L97 was not covered by tests
);
}
20 changes: 20 additions & 0 deletions lib/src/query_parameters/search_query.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:meilisearch/meilisearch.dart';
import 'package:meilisearch/src/annotations.dart';
import 'queryable.dart';

class SearchQuery extends Queryable {
Expand All @@ -20,6 +21,12 @@
final String? highlightPostTag;
final MatchingStrategy? matchingStrategy;
final List<String>? attributesToSearchOn;
@RequiredMeiliServerVersion('1.3.0')
final bool? showRankingScore;
@RequiredMeiliServerVersion('1.3.0')
final bool? showRankingScoreDetails;
@RequiredMeiliServerVersion('1.3.0')
final List<dynamic /* double | List<double> */ >? vector;

const SearchQuery({
this.offset,
Expand All @@ -40,6 +47,9 @@
this.highlightPostTag,
this.matchingStrategy,
this.attributesToSearchOn,
this.showRankingScore,
this.showRankingScoreDetails,
this.vector,
});

@override
Expand All @@ -62,6 +72,9 @@
'highlightPostTag': highlightPostTag,
'matchingStrategy': matchingStrategy?.name,
'attributesToSearchOn': attributesToSearchOn,
'showRankingScore': showRankingScore,
'showRankingScoreDetails': showRankingScoreDetails,
'vector': vector,
};
}

Expand All @@ -84,6 +97,9 @@
String? highlightPostTag,
MatchingStrategy? matchingStrategy,
List<String>? attributesToSearchOn,
bool? showRankingScore,
List<dynamic>? vector,
bool? showRankingScoreDetails,
}) =>
SearchQuery(
offset: offset ?? this.offset,
Expand All @@ -105,5 +121,9 @@
highlightPostTag: highlightPostTag ?? this.highlightPostTag,
matchingStrategy: matchingStrategy ?? this.matchingStrategy,
attributesToSearchOn: attributesToSearchOn ?? this.attributesToSearchOn,
showRankingScore: showRankingScore ?? this.showRankingScore,
vector: vector ?? this.vector,

Check warning on line 125 in lib/src/query_parameters/search_query.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/query_parameters/search_query.dart#L124-L125

Added lines #L124 - L125 were not covered by tests
showRankingScoreDetails:
showRankingScoreDetails ?? this.showRankingScoreDetails,

Check warning on line 127 in lib/src/query_parameters/search_query.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/query_parameters/search_query.dart#L127

Added line #L127 was not covered by tests
);
}
3 changes: 3 additions & 0 deletions lib/src/results/_exports.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ export 'matching_strategy_enum.dart';
export 'index_stats.dart';
export 'all_stats.dart';
export 'facet_stat.dart';
export 'document_container.dart';
export 'ranking_rules/_exports.dart';
export 'experimental_features.dart';
163 changes: 163 additions & 0 deletions lib/src/results/document_container.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import 'package:meilisearch/src/annotations.dart';

import 'match_position.dart';
import 'ranking_rules/base.dart';
import 'searchable.dart';

/// A class that wraps around documents returned from meilisearch to provide useful information.
final class MeiliDocumentContainer<T extends Object> {
const MeiliDocumentContainer._({
required this.rankingScoreDetails,
required this.src,
required this.parsed,
required this.formatted,
required this.vectors,
required this.semanticScore,
required this.rankingScore,
required this.matchesPosition,
});

final Map<String, dynamic> src;
final T parsed;
final Map<String, dynamic>? formatted;
@RequiredMeiliServerVersion('1.3.0')
final List<dynamic /* double | List<double> */ >? vectors;
@RequiredMeiliServerVersion('1.3.0')
final double? semanticScore;
@RequiredMeiliServerVersion('1.3.0')
final double? rankingScore;
@RequiredMeiliServerVersion('1.3.0')
final MeiliRankingScoreDetails? rankingScoreDetails;

/// Contains the location of each occurrence of queried terms across all fields
final Map<String, List<MatchPosition>>? matchesPosition;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was incorrectly placed in Searcheable<T>


dynamic operator [](String key) => src[key];
dynamic getFormatted(String key) => formatted?[key];

Check warning on line 36 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L35-L36

Added lines #L35 - L36 were not covered by tests

dynamic getFormattedOrSrc(String key) => getFormatted(key) ?? this[key];

Check warning on line 38 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L38

Added line #L38 was not covered by tests
Comment on lines +35 to +38
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convenience methods


static MeiliDocumentContainer<Map<String, dynamic>> fromJson(
Map<String, dynamic> src,
) {
final rankingScoreDetails =
src['_rankingScoreDetails'] as Map<String, dynamic>?;
return MeiliDocumentContainer<Map<String, dynamic>>._(
src: src,
parsed: src,
formatted: src['_formatted'] as Map<String, dynamic>?,
vectors: src['_vectors'] as List?,
semanticScore: src['_semanticScore'] as double?,
rankingScore: src['_rankingScore'] as double?,
matchesPosition: _readMatchesPosition(src),
rankingScoreDetails: rankingScoreDetails == null
? null
: MeiliRankingScoreDetails.fromJson(rankingScoreDetails),
);
}

MeiliDocumentContainer<TOther> map<TOther extends Object>(

Check warning on line 59 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L59

Added line #L59 was not covered by tests
MeilisearchDocumentMapper<T, TOther> mapper,
) {
return MeiliDocumentContainer._(
src: src,
parsed: mapper(parsed),
formatted: formatted,
vectors: vectors,
semanticScore: semanticScore,
rankingScore: rankingScore,
rankingScoreDetails: rankingScoreDetails,
matchesPosition: matchesPosition);

Check warning on line 70 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L62-L70

Added lines #L62 - L70 were not covered by tests
}

@override
String toString() => src.toString();

Check warning on line 74 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L73-L74

Added lines #L73 - L74 were not covered by tests
}

class MeiliRankingScoreDetails {
const MeiliRankingScoreDetails._({
required this.src,
required this.words,
required this.typo,
required this.proximity,
required this.attribute,
required this.exactness,
required this.customRules,
});
final Map<String, dynamic> src;
final MeiliRankingScoreDetailsWordsRule? words;
final MeiliRankingScoreDetailsTypoRule? typo;
final MeiliRankingScoreDetailsProximityRule? proximity;
final MeiliRankingScoreDetailsAttributeRule? attribute;
final MeiliRankingScoreDetailsExactnessRule? exactness;
final Map<String, MeiliRankingScoreDetailsCustomRule> customRules;

factory MeiliRankingScoreDetails.fromJson(Map<String, dynamic> src) {
final reservedKeys = {
'attribute',
'words',
'exactness',
'proximity',
'typo',
};

T? ruleGuarded<T>(
String key,
T Function(Map<String, dynamic> src) mapper,
) {
final v = src[key];
if (v == null) {
return null;
}
return mapper(v as Map<String, dynamic>);
}

return MeiliRankingScoreDetails._(
src: src,
attribute: ruleGuarded(
'attribute',
MeiliRankingScoreDetailsAttributeRule.fromJson,
),
words: ruleGuarded(
'words',
MeiliRankingScoreDetailsWordsRule.fromJson,
),
exactness: ruleGuarded(
'exactness',
MeiliRankingScoreDetailsExactnessRule.fromJson,
),
proximity: ruleGuarded(
'proximity',
MeiliRankingScoreDetailsProximityRule.fromJson,
),
typo: ruleGuarded(
'typo',
MeiliRankingScoreDetailsTypoRule.fromJson,
),
customRules: {
for (var custom in src.entries
.where((element) => !reservedKeys.contains(element.key)))
custom.key: MeiliRankingScoreDetailsCustomRule.fromJson(
custom.value as Map<String, dynamic>,

Check warning on line 141 in lib/src/results/document_container.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/results/document_container.dart#L140-L141

Added lines #L140 - L141 were not covered by tests
)
Comment on lines +138 to +142
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't had a chance to test this, since I have no idea when a custom rule is returned

},
);
}
}

Map<String, List<MatchPosition>>? _readMatchesPosition(
Map<String, Object?> map,
) {
final src = map['_matchesPosition'];

if (src == null) return null;

return (src as Map<String, Object?>).map(
(key, value) => MapEntry(
key,
(value as List<Object?>)
.map((e) => MatchPosition.fromMap(e as Map<String, Object?>))
.toList(),
),
);
}
42 changes: 42 additions & 0 deletions lib/src/results/experimental_features.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:json_annotation/json_annotation.dart';

part 'experimental_features.g.dart';

@JsonSerializable(
createFactory: true,
createToJson: false,
)
class ExperimentalFeatures {
@JsonKey(name: 'vectorStore')
final bool vectorStore;
@JsonKey(name: 'scoreDetails')
final bool scoreDetails;

const ExperimentalFeatures({
required this.vectorStore,
required this.scoreDetails,
});

factory ExperimentalFeatures.fromJson(Map<String, dynamic> src) {
return _$ExperimentalFeaturesFromJson(src);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is generated using json_serializable

}
}

@JsonSerializable(
includeIfNull: false,
createToJson: true,
createFactory: false,
)
class UpdateExperimentalFeatures {
@JsonKey(name: 'vectorStore')
final bool? vectorStore;
@JsonKey(name: 'scoreDetails')
final bool? scoreDetails;

const UpdateExperimentalFeatures({
this.vectorStore,
this.scoreDetails,
});

Map<String, dynamic> toJson() => _$UpdateExperimentalFeaturesToJson(this);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is generated using json_serializable

}
Loading