diff --git a/README.md b/README.md index 684980b..3f24296 100644 --- a/README.md +++ b/README.md @@ -13,21 +13,25 @@ Documentation -[Meilisearch](https://github.com/meilisearch/meilisearch) is a lightning-fast search engine that fits effortlessly into your apps, websites, and workflow. +[Meilisearch](https://github.com/meilisearch/meilisearch) is a lightning-fast search engine that fits effortlessly into +your apps, websites, and workflow. -> The goal of this library is to provide a `object-oriented`, `semantic`, `reactive`, and `streamlined` meilisearch-rest-client, supporting reactivity through reactor-netty-http. +> The goal of this library is to provide a `object-oriented`, `semantic`, `reactive`, and `streamlined` +> meilisearch-rest-client, supporting reactivity through reactor-netty-http. ### Version + The version number of this library is named by appending `.X` to the version number in the official documentation. -- v1.7 Docs: [1.7.0.x,) (*current) -- v1.6 Docs: [1.6.0.x, 1.7.0.0) -- v1.5 Docs: [1.5.0.x, 1.6.0.0) +| Doc's Version | Library Version | +|---------------|-------------------| +| V1.7 | 1.7.0.0 + | +| V1.6 | 1.6.0.0 ~ 1.7.0.0 | +| V1.5 | 1.5.0.0 ~ 1.6.0.0 | ### Dependencies -*By default, this library depends on libraries as fallows:* - +**By default**, this library depends on libraries as fallows: - reactor-netty-http(required) - jackson(replaceable by providing implementation of `io.github.honhimw.ms.JsonHandler`) @@ -35,7 +39,7 @@ The version number of this library is named by appending `.X` to the version num ```shell # build from sources -$ gradlew clean build -x test +$ ./gradlew clean build -x test ``` ```groovy @@ -57,71 +61,69 @@ implementation 'io.github.honhimw:meilisearch-rest-client:1.7.0.0-RC.1' ## Usage #### Reactive(reactor) + ```java public static void main(String[] args) { JsonHandler jsonHandler = new JacksonJsonHandler(); - try ( - ReactiveMSearchClient client = ReactiveMSearchClient.create(builder -> builder - .enableSSL(false) // true: https, false: http - .host("{{meilisearch-server-host}}") // server host - .port(7700) // server port - .jsonHandler(jsonHandler) - .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))) - ) { - String indexUid = "movies"; - Mono> searchResponse = client.indexes(indexes -> indexes - .search(indexUid, search -> search - .find("hello world", Movie.class))); - List hits = searchResponse.block().getHits(); - // or - List hits2 = client.indexes(indexes -> indexes - .search(indexUid, Movie.class, search -> search - .find(q -> q - .q("hello world") - .limit(1) - ) - .map(SearchResponse::getHits) + @Cleanup + ReactiveMSearchClient client = ReactiveMSearchClient.create(builder -> builder + .enableSSL(false) // true: https, false: http + .host("{{meilisearch-server-host}}") // server host + .port(7700) // server port + .jsonHandler(jsonHandler) + .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))); + String indexUid = "movies"; + Mono> searchResponse = client.indexes(indexes -> indexes + .search(indexUid, search -> search + .find("hello world", Movie.class))); + List hits = searchResponse.block().getHits(); + // or + List hits2 = client.indexes(indexes -> indexes + .search(indexUid, Movie.class, search -> search + .find(q -> q + .q("hello world") + .limit(1) ) - ).block(); - } + .map(SearchResponse::getHits) + ) + ).block(); } ``` #### Blocking + ```java public static void main(String[] args) { JsonHandler jsonHandler = new JacksonJsonHandler(); - try ( - MSearchClient client = MSearchClient.create(builder -> builder - .enableSSL(false) // true: https, false: http - .host("{{meilisearch-server-host}}") // server host - .port(7700) // server port - .jsonHandler(jsonHandler)) - .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))) - ) { - String indexUid = "movies"; - SearchResponse searchResponse = client.indexes(indexes -> indexes - .search(indexUid, search -> search - .find("hello world", Movie.class))); - List hits = searchResponse.getHits(); - // or - List hits2 = client.indexes(indexes -> indexes - .search(indexUid, Movie.class, search -> search - .find(q -> q - .q("hello world") - .limit(1) - ) - .getHits() + @Cleanup + MSearchClient client = MSearchClient.create(builder -> builder + .enableSSL(false) // true: https, false: http + .host("{{meilisearch-server-host}}") // server host + .port(7700) // server port + .jsonHandler(jsonHandler) + .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))); + String indexUid = "movies"; + SearchResponse searchResponse = client.indexes(indexes -> indexes + .search(indexUid, search -> search + .find("hello world", Movie.class))); + List hits = searchResponse.getHits(); + // or + List hits2 = client.indexes(indexes -> indexes + .search(indexUid, Movie.class, search -> search + .find(q -> q + .q("hello world") + .limit(1) ) - ); - } + .getHits() + ) + ); } ``` ## Run Tests ```shell -$ gradlew test +$ ./gradlew test ``` Create file named `profile-test.properties` under project root directory. @@ -129,12 +131,11 @@ Create file named `profile-test.properties` under project root directory. ```properties ./meilisearch-rest-client └── profile-test.properties - meili-search.host=127.0.0.1 meili-search.port=7700 -meili-search.api-key= +meili-search.api-key=MASTER_KEY ``` -**Note**: You may also set `profiles.active` in gradle.properties for loading different properties file such as: +**Note**: You may also set `profiles.active` in gradle.properties for loading different properties file such as: > profile-alpha.properties: by setting profiles.active=alpha > profile-beta.properties: by setting profiles.active=beta diff --git a/build.gradle b/build.gradle index 8a43197..15886b0 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,7 @@ def reactorVersion = '2023.0.3' def jacksonVersion = '2.16.1' def gsonVersion = '2.10.1' def lombokVersion = '1.18.30' +def slf4jVersion = '2.0.12' apply plugin: 'idea' apply plugin: 'java' @@ -44,7 +45,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-annotations' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - implementation 'org.slf4j:slf4j-api:2.0.12' + implementation "org.slf4j:slf4j-api:${slf4jVersion}" compileOnly "com.google.code.gson:gson:${gsonVersion}" @@ -54,7 +55,6 @@ dependencies { compileOnly "org.projectlombok:lombok:${lombokVersion}" annotationProcessor "org.projectlombok:lombok:${lombokVersion}" - } dependencies { @@ -67,7 +67,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}" testImplementation 'io.projectreactor:reactor-test' - testImplementation 'org.slf4j:slf4j-simple:2.0.12' + testImplementation "org.slf4j:slf4j-simple:${slf4jVersion}" } java { @@ -168,7 +168,6 @@ jar { tasks.withType(JavaCompile).configureEach { options.encoding = StandardCharsets.UTF_8.name() inputs.files(tasks.withType(ProcessResources)) - } test { @@ -205,18 +204,18 @@ jacocoTestReport { } tasks.register('publish-snapshot') { - version = version + '-SNAPSHOT' - dependsOn tasks.getByPath(':publishAllPublicationsToSonatype-snapshotsRepository') + version = version + '-RC.' + rc + '-SNAPSHOT' + finalizedBy ':publishAllPublicationsToSonatype-snapshotsRepository' } tasks.register('publish-rc') { version = version + '-RC.' + rc - dependsOn tasks.getByPath(':publishAllPublicationsToSonatype-central-rcRepository') + finalizedBy ':publishAllPublicationsToSonatype-central-rcRepository' } tasks.register('publish-release') { - dependsOn tasks.getByPath(':publishAllPublicationsToSonatype-releasesRepository') + finalizedBy ':publishAllPublicationsToSonatype-releasesRepository' } tasks.register('publish-central') { - dependsOn tasks.getByPath(':publishAllPublicationsToSonatype-centralRepository') + finalizedBy ':publishAllPublicationsToSonatype-centralRepository' } Properties loadProfileProperties() { diff --git a/gradle.properties b/gradle.properties index 6337847..e1e96c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,7 @@ # limitations under the License. # +# Loading properties file from ./profile-test.properties profiles.active=test # Gradle diff --git a/src/main/java/io/github/honhimw/ms/api/Indexes.java b/src/main/java/io/github/honhimw/ms/api/Indexes.java index a2f8d34..fd83635 100644 --- a/src/main/java/io/github/honhimw/ms/api/Indexes.java +++ b/src/main/java/io/github/honhimw/ms/api/Indexes.java @@ -106,6 +106,13 @@ public interface Indexes { @Operation(method = "POST", tags = "/swap-indexes") TaskInfo swap(Consumer consumer); + /** + * Get the operator for a single index. + * @param uid index uid + * @return {@link SingleIndex} operator + */ + SingleIndex single(String uid); + /** * Documents are objects composed of fields that can store any type of data. * Each field contains an attribute and its associated value. diff --git a/src/main/java/io/github/honhimw/ms/api/SingleIndex.java b/src/main/java/io/github/honhimw/ms/api/SingleIndex.java new file mode 100644 index 0000000..a002f00 --- /dev/null +++ b/src/main/java/io/github/honhimw/ms/api/SingleIndex.java @@ -0,0 +1,158 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.honhimw.ms.api; + +import io.github.honhimw.ms.json.TypeRef; +import io.github.honhimw.ms.model.Index; +import io.github.honhimw.ms.model.IndexStats; +import io.github.honhimw.ms.model.TaskInfo; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Nullable; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Single Index operator. + * + * @author hon_him + * @since 2024-03-08 + */ + +public interface SingleIndex { + + /** + * Get information about current index. + */ + @Operation(method = "GET", tags = "/indexes/{index_uid}") + Optional get(); + + /** + * Create an index. + * + * @param primaryKey Primary key of current index + * @return create task + */ + @Operation(method = "POST", tags = "/indexes") + TaskInfo create(@Nullable String primaryKey); + + /** + * Update current index. Specify a primaryKey if it doesn't already exists yet. + * + * @return update task + */ + @Operation(method = "PATCH", tags = "/indexes/{index_uid}") + TaskInfo update(String primaryKey); + + /** + * Delete current index. + * + * @return delete task + */ + @Operation(method = "DELETE", tags = "/indexes/{index_uid}") + TaskInfo delete(); + + /** + * Documents are objects composed of fields that can store any type of data. + * Each field contains an attribute and its associated value. + * Documents are stored inside indexes. + * Learn more about documents. + * + * @return {@link Documents} operator + */ + @Operation(tags = "/indexes/{index_uid}/documents") + Documents documents(); + + default R documents(Function operation) { + return operation.apply(documents()); + } + + TypedDocuments documents(TypeRef typeRef); + + default TypedDocuments documents(Class type) { + return documents(TypeRef.of(type)); + } + + default R documents(TypeRef typeRef, Function, R> operation) { + return operation.apply(documents(typeRef)); + } + + default R documents(Class type, Function, R> operation) { + return operation.apply(documents(type)); + } + + /** + * Meilisearch exposes 3 routes to perform document searches: + *
    + *
  • A POST route: this is the preferred route when using API authentication, as it allows preflight request caching and better performances.
  • + *
  • A GET route: the usage of this route is discouraged, unless you have good reason to do otherwise (specific caching abilities for example). Other than the differences mentioned above, the two routes are strictly equivalent.
  • + *
  • A POST multi-search route allowing to perform multiple search queries in a single HTTP request. Meilisearch exposes 1 route to perform facet searches:
  • + *
  • A POST facet-search route allowing to perform a facet search query on a facet in a single HTTP request.
  • + *
+ * + * @return {@link Search} operator + */ + @Operation(tags = "/indexes/{index_uid}/search") + Search search(); + + default R search(Function operation) { + return operation.apply(search()); + } + + TypedSearch search(TypeRef typeRef); + + default TypedSearch search(Class type) { + return search(TypeRef.of(type)); + } + + default R search(TypeRef typeRef, Function, R> operation) { + return operation.apply(search(typeRef)); + } + + default R search(Class type, Function, R> operation) { + return operation.apply(search(type)); + } + + TypedDetailsSearch searchWithDetails(TypeRef typeRef); + + default TypedDetailsSearch searchWithDetails(Class type) { + return searchWithDetails(TypeRef.of(type)); + } + + default R searchWithDetails(TypeRef typeRef, Function, R> operation) { + return operation.apply(searchWithDetails(typeRef)); + } + + default R searchWithDetails(Class type, Function, R> operation) { + return operation.apply(searchWithDetails(type)); + } + + @Operation(tags = "/indexes/{indexUid}/settings") + Settings settings(); + + default R settings(Function operation) { + return operation.apply(settings()); + } + + /** + * Get stats of current index. + * + * @return stats of current index. + */ + @Operation(method = "GET", tags = "/indexes/{index_uid}/stats") + IndexStats stats(); + + +} diff --git a/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveIndexes.java b/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveIndexes.java index fa1d755..90c7418 100644 --- a/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveIndexes.java +++ b/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveIndexes.java @@ -118,6 +118,13 @@ default Mono swap(Consumer consumer) { return swap(entryList.getList()); } + /** + * Get the operator for a single index. + * @param uid index uid + * @return {@link ReactiveSingleIndex} operator + */ + ReactiveSingleIndex single(String uid); + /** * Documents are objects composed of fields that can store any type of data. * Each field contains an attribute and its associated value. diff --git a/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveSingleIndex.java b/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveSingleIndex.java new file mode 100644 index 0000000..a244ad4 --- /dev/null +++ b/src/main/java/io/github/honhimw/ms/api/reactive/ReactiveSingleIndex.java @@ -0,0 +1,158 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.honhimw.ms.api.reactive; + +import io.github.honhimw.ms.json.TypeRef; +import io.github.honhimw.ms.model.Index; +import io.github.honhimw.ms.model.IndexStats; +import io.github.honhimw.ms.model.TaskInfo; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Nullable; +import reactor.core.publisher.Mono; + +import java.util.function.Function; + +/** + * Single Index operator. + * + * @author hon_him + * @since 2024-03-08 + */ + +public interface ReactiveSingleIndex { + + /** + * Get information about current index. + */ + @Operation(method = "GET", tags = "/indexes/{index_uid}") + Mono get(); + + /** + * Create an index. + * + * @param primaryKey Primary key of current index + * @return create task + */ + @Operation(method = "POST", tags = "/indexes") + Mono create(@Nullable String primaryKey); + + /** + * Update current index. Specify a primaryKey if it doesn't already exists yet. + * + * @return update task + */ + @Operation(method = "PATCH", tags = "/indexes/{index_uid}") + Mono update(String primaryKey); + + /** + * Delete current index. + * + * @return delete task + */ + @Operation(method = "DELETE", tags = "/indexes/{index_uid}") + Mono delete(); + + /** + * Documents are objects composed of fields that can store any type of data. + * Each field contains an attribute and its associated value. + * Documents are stored inside indexes. + * Learn more about documents. + * + * @return {@link ReactiveDocuments} operator + */ + @Operation(tags = "/indexes/{index_uid}/documents") + ReactiveDocuments documents(); + + default R documents(Function operation) { + return operation.apply(documents()); + } + + ReactiveTypedDocuments documents(TypeRef typeRef); + + default ReactiveTypedDocuments documents(Class type) { + return documents(TypeRef.of(type)); + } + + default R documents(TypeRef typeRef, Function, R> operation) { + return operation.apply(documents(typeRef)); + } + + default R documents(Class type, Function, R> operation) { + return operation.apply(documents(type)); + } + + /** + * Meilisearch exposes 3 routes to perform document searches: + *
    + *
  • A POST route: this is the preferred route when using API authentication, as it allows preflight request caching and better performances.
  • + *
  • A GET route: the usage of this route is discouraged, unless you have good reason to do otherwise (specific caching abilities for example). Other than the differences mentioned above, the two routes are strictly equivalent.
  • + *
  • A POST multi-search route allowing to perform multiple search queries in a single HTTP request. Meilisearch exposes 1 route to perform facet searches:
  • + *
  • A POST facet-search route allowing to perform a facet search query on a facet in a single HTTP request.
  • + *
+ * + * @return {@link ReactiveSearch} operator + */ + @Operation(tags = "/indexes/{index_uid}/search") + ReactiveSearch search(); + + default R search(Function operation) { + return operation.apply(search()); + } + + ReactiveTypedSearch search(TypeRef typeRef); + + default ReactiveTypedSearch search(Class type) { + return search(TypeRef.of(type)); + } + + default R search(TypeRef typeRef, Function, R> operation) { + return operation.apply(search(typeRef)); + } + + default R search(Class type, Function, R> operation) { + return operation.apply(search(type)); + } + + ReactiveTypedDetailsSearch searchWithDetails(TypeRef typeRef); + + default ReactiveTypedDetailsSearch searchWithDetails(Class type) { + return searchWithDetails(TypeRef.of(type)); + } + + default R searchWithDetails(TypeRef typeRef, Function, R> operation) { + return operation.apply(searchWithDetails(typeRef)); + } + + default R searchWithDetails(Class type, Function, R> operation) { + return operation.apply(searchWithDetails(type)); + } + + @Operation(tags = "/indexes/{indexUid}/settings") + ReactiveSettings settings(); + + default R settings(Function operation) { + return operation.apply(settings()); + } + + /** + * Get stats of current index. + * + * @return stats of current index. + */ + @Operation(method = "GET", tags = "/indexes/{index_uid}/stats") + Mono stats(); + + +} diff --git a/src/main/java/io/github/honhimw/ms/internal/IndexesImpl.java b/src/main/java/io/github/honhimw/ms/internal/IndexesImpl.java index ca6641b..8c3061d 100644 --- a/src/main/java/io/github/honhimw/ms/internal/IndexesImpl.java +++ b/src/main/java/io/github/honhimw/ms/internal/IndexesImpl.java @@ -90,6 +90,11 @@ public TaskInfo swap(Consumer consumer) { return ReactorUtils.blockNonNull(_indexes.swap(consumer)); } + @Override + public SingleIndex single(String uid) { + return new SingleIndexImpl(_indexes.single(uid)); + } + @Override public Documents documents(String uid) { return new DocumentsImpl(_indexes.documents(uid)); diff --git a/src/main/java/io/github/honhimw/ms/internal/SingleIndexImpl.java b/src/main/java/io/github/honhimw/ms/internal/SingleIndexImpl.java new file mode 100644 index 0000000..8295dee --- /dev/null +++ b/src/main/java/io/github/honhimw/ms/internal/SingleIndexImpl.java @@ -0,0 +1,156 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.honhimw.ms.internal; + +import io.github.honhimw.ms.api.*; +import io.github.honhimw.ms.api.reactive.ReactiveSingleIndex; +import io.github.honhimw.ms.json.TypeRef; +import io.github.honhimw.ms.model.Index; +import io.github.honhimw.ms.model.IndexStats; +import io.github.honhimw.ms.model.TaskInfo; +import io.github.honhimw.ms.support.ReactorUtils; +import jakarta.annotation.Nullable; + +import java.util.Optional; +import java.util.function.Function; + +/** + * @author hon_him + * @since 2024-03-08 + */ + +class SingleIndexImpl implements SingleIndex { + + private final ReactiveSingleIndex _index; + + protected SingleIndexImpl(ReactiveSingleIndex index) { + this._index = index; + } + + @Override + public Optional get() { + return _index.get().blockOptional(); + } + + @Override + public TaskInfo create(@Nullable String primaryKey) { + return ReactorUtils.blockNonNull(_index.create(primaryKey)); + } + + @Override + public TaskInfo update(String primaryKey) { + return ReactorUtils.blockNonNull(_index.update(primaryKey)); + } + + @Override + public TaskInfo delete() { + return ReactorUtils.blockNonNull(_index.delete()); + } + + @Override + public Documents documents() { + return new DocumentsImpl(_index.documents()); + } + + @Override + public R documents(Function operation) { + return operation.apply(documents()); + } + + @Override + public TypedDocuments documents(TypeRef typeRef) { + return new TypedDocumentImpl<>(_index.documents(typeRef)); + } + + @Override + public TypedDocuments documents(Class type) { + return documents(TypeRef.of(type)); + } + + @Override + public R documents(TypeRef typeRef, Function, R> operation) { + return operation.apply(documents(typeRef)); + } + + @Override + public R documents(Class type, Function, R> operation) { + return operation.apply(documents(type)); + } + + @Override + public Search search() { + return new SearchImpl(_index.search()); + } + + @Override + public R search(Function operation) { + return operation.apply(search()); + } + + @Override + public TypedSearch search(TypeRef typeRef) { + return new TypedSearchImpl<>(_index.search(typeRef)); + } + + @Override + public TypedSearch search(Class type) { + return search(TypeRef.of(type)); + } + + @Override + public R search(TypeRef typeRef, Function, R> operation) { + return operation.apply(search(typeRef)); + } + + @Override + public R search(Class type, Function, R> operation) { + return operation.apply(search(type)); + } + + @Override + public TypedDetailsSearch searchWithDetails(TypeRef typeRef) { + return new TypedDetailsSearchImpl<>(_index.searchWithDetails(typeRef)); + } + + @Override + public TypedDetailsSearch searchWithDetails(Class type) { + return searchWithDetails(TypeRef.of(type)); + } + + @Override + public R searchWithDetails(TypeRef typeRef, Function, R> operation) { + return operation.apply(searchWithDetails(typeRef)); + } + + @Override + public R searchWithDetails(Class type, Function, R> operation) { + return operation.apply(searchWithDetails(type)); + } + + @Override + public Settings settings() { + return new SettingsImpl(_index.settings()); + } + + @Override + public R settings(Function operation) { + return operation.apply(settings()); + } + + @Override + public IndexStats stats() { + return ReactorUtils.blockNonNull(_index.stats()); + } +} diff --git a/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveIndexesImpl.java b/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveIndexesImpl.java index 9bf2480..76a1a93 100644 --- a/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveIndexesImpl.java +++ b/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveIndexesImpl.java @@ -111,6 +111,11 @@ public Mono swap(List> uids) { })), TypeRefs.TaskInfoRef.INSTANCE); } + @Override + public ReactiveSingleIndex single(String uid) { + return new ReactiveSingleIndexImpl(uid, this); + } + @Override public ReactiveDocuments documents(String uid) { return new ReactiveDocumentsImpl(this, uid); diff --git a/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveSingleIndexImpl.java b/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveSingleIndexImpl.java new file mode 100644 index 0000000..bdfa138 --- /dev/null +++ b/src/main/java/io/github/honhimw/ms/internal/reactive/ReactiveSingleIndexImpl.java @@ -0,0 +1,109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.honhimw.ms.internal.reactive; + +import io.github.honhimw.ms.api.reactive.*; +import io.github.honhimw.ms.json.TypeRef; +import io.github.honhimw.ms.model.Index; +import io.github.honhimw.ms.model.IndexStats; +import io.github.honhimw.ms.model.TaskInfo; +import jakarta.annotation.Nullable; +import reactor.core.publisher.Mono; + +/** + * @author hon_him + * @since 2024-03-08 + */ + +class ReactiveSingleIndexImpl extends AbstractReactiveImpl implements ReactiveSingleIndex { + + private final String uid; + private final ReactiveIndexesImpl indexes; + + protected ReactiveSingleIndexImpl(String uid, ReactiveIndexesImpl indexes) { + super(indexes._client); + this.uid = uid; + this.indexes = indexes; + } + + @Override + public Mono get() { + return indexes.get(uid); + } + + @Override + public Mono create(@Nullable String primaryKey) { + return indexes.create(uid, primaryKey); + } + + @Override + public Mono update(String primaryKey) { + return indexes.update(uid, primaryKey); + } + + @Override + public Mono delete() { + return indexes.delete(uid); + } + + @Override + public ReactiveDocuments documents() { + return indexes.documents(uid); + } + + @Override + public ReactiveTypedDocuments documents(TypeRef typeRef) { + return indexes.documents(uid, typeRef); + } + + @Override + public ReactiveSearch search() { + return indexes.search(uid); + } + + @Override + public ReactiveTypedSearch search(TypeRef typeRef) { + return indexes.search(uid, typeRef); + } + + @Override + public ReactiveTypedDetailsSearch searchWithDetails(TypeRef typeRef) { + return indexes.searchWithDetails(uid, typeRef); + } + + @Override + public ReactiveSettings settings() { + return indexes.settings(uid); + } + + @Override + public Mono stats() { + return indexes.stats(uid); + } +} diff --git a/src/main/java/io/github/honhimw/ms/json/JacksonJsonHandler.java b/src/main/java/io/github/honhimw/ms/json/JacksonJsonHandler.java index 347f209..a50e5d6 100644 --- a/src/main/java/io/github/honhimw/ms/json/JacksonJsonHandler.java +++ b/src/main/java/io/github/honhimw/ms/json/JacksonJsonHandler.java @@ -53,14 +53,14 @@ @Getter public class JacksonJsonHandler implements JsonHandler { - private final ObjectMapper objectMapper; + private final JsonMapper jsonMapper; public JacksonJsonHandler() { this(defaultBuilder().build()); } - public JacksonJsonHandler(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public JacksonJsonHandler(JsonMapper jsonMapper) { + this.jsonMapper = jsonMapper; } @SuppressWarnings("rawtypes") @@ -141,7 +141,7 @@ public Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOE @Override public String toJson(Object o) { try { - return objectMapper.writeValueAsString(o); + return jsonMapper.writeValueAsString(o); } catch (Exception e) { throw new IllegalArgumentException("json encode exception", e); } @@ -150,7 +150,7 @@ public String toJson(Object o) { @Override public T fromJson(String json, Class tClass) { try { - return objectMapper.readValue(json, tClass); + return jsonMapper.readValue(json, tClass); } catch (Exception e) { throw new IllegalArgumentException("json decode exception", e); } @@ -159,7 +159,7 @@ public T fromJson(String json, Class tClass) { @Override public T fromJson(String json, TypeRef typeRef) { try { - return objectMapper.readValue(json, new TypeReference() { + return jsonMapper.readValue(json, new TypeReference() { @Override public Type getType() { return typeRef.getType(); @@ -173,7 +173,7 @@ public Type getType() { @Override public T transform(Object o, TypeRef typeRef) { try { - return objectMapper.convertValue(o, new TypeReference() { + return jsonMapper.convertValue(o, new TypeReference() { @Override public Type getType() { return typeRef.getType(); @@ -183,4 +183,9 @@ public Type getType() { throw new IllegalArgumentException("json decode exception", e); } } + + public JsonMapper getJsonMapper() { + return jsonMapper; + } + } diff --git a/src/main/java/io/github/honhimw/ms/model/RankingRule.java b/src/main/java/io/github/honhimw/ms/model/RankingRule.java index 4768d1e..7eb45fd 100644 --- a/src/main/java/io/github/honhimw/ms/model/RankingRule.java +++ b/src/main/java/io/github/honhimw/ms/model/RankingRule.java @@ -16,6 +16,10 @@ import io.github.honhimw.ms.json.EnumValue; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * Ranking rules array * @@ -60,4 +64,8 @@ public String value() { return this.name; } + public static List defaultOrder() { + return Arrays.asList(WORDS, TYPO, PROXIMITY, ATTRIBUTE, SORT, EXACTNESS); + } + } diff --git a/src/test/java/io/github/honhimw/ms/MainRunner.java b/src/test/java/io/github/honhimw/ms/MainRunner.java index 5d901d8..26dd01d 100644 --- a/src/test/java/io/github/honhimw/ms/MainRunner.java +++ b/src/test/java/io/github/honhimw/ms/MainRunner.java @@ -22,6 +22,7 @@ import io.github.honhimw.ms.json.JsonHandler; import io.github.honhimw.ms.json.TypeRef; import io.github.honhimw.ms.model.SearchResponse; +import lombok.Cleanup; import reactor.core.publisher.Mono; import java.time.Duration; @@ -52,58 +53,54 @@ public static TypeRef> type(TypeRef typeRef) { public static void reactive() { JsonHandler jsonHandler = new JacksonJsonHandler(); - try ( - ReactiveMSearchClient client = ReactiveMSearchClient.create(builder -> builder - .enableSSL(false) // true: https, false: http - .host("{{meilisearch-server-host}}") // server host - .port(7700) // server port - .jsonHandler(jsonHandler) - .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))) - ) { - String indexUid = "movies"; - Mono> searchResponse = client.indexes(indexes -> indexes - .search(indexUid, search -> search - .find("hello world", Movie.class))); - List hits = searchResponse.block().getHits(); - // or - List hits2 = client.indexes(indexes -> indexes - .search(indexUid, Movie.class, search -> search - .find(q -> q - .q("hello world") - .limit(1) - ) - .map(SearchResponse::getHits) + @Cleanup + ReactiveMSearchClient client = ReactiveMSearchClient.create(builder -> builder + .enableSSL(false) // true: https, false: http + .host("{{meilisearch-server-host}}") // server host + .port(7700) // server port + .jsonHandler(jsonHandler) + .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))); + String indexUid = "movies"; + Mono> searchResponse = client.indexes(indexes -> indexes + .search(indexUid, search -> search + .find("hello world", Movie.class))); + List hits = searchResponse.block().getHits(); + // or + List hits2 = client.indexes(indexes -> indexes + .search(indexUid, Movie.class, search -> search + .find(q -> q + .q("hello world") + .limit(1) ) - ).block(); - } + .map(SearchResponse::getHits) + ) + ).block(); } public static void blocking() { JsonHandler jsonHandler = new JacksonJsonHandler(); - try ( - MSearchClient client = MSearchClient.create(builder -> builder - .enableSSL(false) // true: https, false: http - .host("{{meilisearch-server-host}}") // server host - .port(7700) // server port - .jsonHandler(jsonHandler) - .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))) - ) { - String indexUid = "movies"; - SearchResponse searchResponse = client.indexes(indexes -> indexes - .search(indexUid, search -> search - .find("hello world", Movie.class))); - List hits = searchResponse.getHits(); - // or - List hits2 = client.indexes(indexes -> indexes - .search(indexUid, Movie.class, search -> search - .find(q -> q - .q("hello world") - .limit(1) - ) - .getHits() + @Cleanup + MSearchClient client = MSearchClient.create(builder -> builder + .enableSSL(false) // true: https, false: http + .host("{{meilisearch-server-host}}") // server host + .port(7700) // server port + .jsonHandler(jsonHandler) + .httpClient(ReactiveHttpUtils.getInstance(http -> http.readTimeout(Duration.ofMillis(100))))); + String indexUid = "movies"; + SearchResponse searchResponse = client.indexes(indexes -> indexes + .search(indexUid, search -> search + .find("hello world", Movie.class))); + List hits = searchResponse.getHits(); + // or + List hits2 = client.indexes(indexes -> indexes + .search(indexUid, Movie.class, search -> search + .find(q -> q + .q("hello world") + .limit(1) ) - ); - } + .getHits() + ) + ); } } diff --git a/src/test/java/io/github/honhimw/ms/client/SingleIndexTests.java b/src/test/java/io/github/honhimw/ms/client/SingleIndexTests.java new file mode 100644 index 0000000..2ee4075 --- /dev/null +++ b/src/test/java/io/github/honhimw/ms/client/SingleIndexTests.java @@ -0,0 +1,87 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.honhimw.ms.client; + +import io.github.honhimw.ms.api.Indexes; +import io.github.honhimw.ms.api.SingleIndex; +import io.github.honhimw.ms.model.Index; +import io.github.honhimw.ms.model.IndexStats; +import io.github.honhimw.ms.model.TaskInfo; +import org.junit.jupiter.api.*; + +import java.util.Optional; + +/** + * @author hon_him + * @since 2024-01-03 + */ + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class SingleIndexTests extends TestBase { + + protected Indexes indexes; + protected SingleIndex singleIndex; + + @BeforeEach + void initIndexes() { + indexes = blockingClient.indexes(); + singleIndex = indexes.single(INDEX); + } + + @Order(-1) + @Test + void delete() { + TaskInfo delete = singleIndex.delete(); + await(delete); + assert !singleIndex.get().isPresent(); + } + + @Order(0) + @Test + void create() { + TaskInfo taskInfo = singleIndex.create("id"); + await(taskInfo); + assert taskInfo.getIndexUid().equals(INDEX); + } + @Order(2) + @Test + void getOne() { + Optional index = singleIndex.get(); + assert index.isPresent(); + } + + @Order(3) + @Test + void update() { + TaskInfo update = singleIndex.update("title"); + await(update); + Optional index = singleIndex.get(); + assert index.isPresent(); + assert index.get().getPrimaryKey().equals("title"); + TaskInfo update1 = singleIndex.update("id"); + await(update1); + Optional index1 = singleIndex.get(); + assert index1.isPresent(); + assert index1.get().getPrimaryKey().equals("id"); + } + + @Order(4) + @Test + void stats() { + IndexStats stats = singleIndex.stats(); + assert !stats.getIsIndexing(): "currently should not in indexing stats."; + } + +}