From 0bd701e40b44bc7485d06c2dd6e4c7722f34e14e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:47:30 -0400 Subject: [PATCH] feat: add lite search API to allow public website search with API key (#7765) * try comparing from origin * feat: add lite search API to allow public website search with API key feat: add LOW_GROUNDED_ANSWER in answer skip reasons feat: support query regex in control match rules docs: keep the API doc up-to-date with recent changes PiperOrigin-RevId: 689588291 Source-Link: https://github.com/googleapis/googleapis/commit/537fd482f6bb8afb3a146d9b21673a8eb27958bd Source-Link: https://github.com/googleapis/googleapis-gen/commit/186fcc4e20a2c06bb514a2ff8d94eb7f7d6470c3 Copy-Tag: eyJwIjoiRGlzY292ZXJ5RW5naW5lLy5Pd2xCb3QueWFtbCIsImgiOiIxODZmY2M0ZTIwYTJjMDZiYjUxNGEyZmY4ZDk0ZWI3ZjdkNjQ3MGMzIn0= Co-authored-by: Brent Shaffer Co-authored-by: Owl Bot --- .../backwards-compatibility-checks.yaml | 2 +- DiscoveryEngine/metadata/V1/Answer.php | Bin 5646 -> 5671 bytes DiscoveryEngine/metadata/V1/Control.php | Bin 2887 -> 2913 bytes DiscoveryEngine/metadata/V1/SearchService.php | Bin 10162 -> 10580 bytes .../V1/SearchServiceClient/search_lite.php | 98 ++++++++++++++++++ .../src/V1/Answer/AnswerSkippedReason.php | 9 ++ .../DocumentMetadata/MatcherValue.php | 6 +- .../src/V1/Client/SearchServiceClient.php | 39 +++++++ DiscoveryEngine/src/V1/Condition.php | 58 +++++++++++ DiscoveryEngine/src/V1/gapic_metadata.json | 5 + .../search_service_client_config.json | 5 + .../search_service_descriptor_config.php | 20 ++++ .../search_service_rest_client_config.php | 24 +++++ .../V1/Client/SearchServiceClientTest.php | 89 ++++++++++++++++ 14 files changed, 351 insertions(+), 4 deletions(-) create mode 100644 DiscoveryEngine/samples/V1/SearchServiceClient/search_lite.php diff --git a/.github/workflows/backwards-compatibility-checks.yaml b/.github/workflows/backwards-compatibility-checks.yaml index 284f12f30e9e..97bda5691608 100644 --- a/.github/workflows/backwards-compatibility-checks.yaml +++ b/.github/workflows/backwards-compatibility-checks.yaml @@ -69,7 +69,7 @@ jobs: # OwlBot PRs which are not labelled feat should not add new files or methods run: | ~/.composer/vendor/bin/roave-backward-compatibility-check \ - --from=${{ github.head_ref || github.ref_name }} \ + --from=origin/${{ github.head_ref || github.ref_name }} \ --to=origin/main --format=github-actions - name: "Print the action item" run: | diff --git a/DiscoveryEngine/metadata/V1/Answer.php b/DiscoveryEngine/metadata/V1/Answer.php index 35be0444f496955fd25ebed50f673099e021e8f4..693a36e0e65356aad5f7787876bcd7b1b35326f6 100644 GIT binary patch delta 59 zcmeCvS+29;G!s*w+T^oLs~B%?u3#?XW4ychy`USTq>wn5u#bOuynB#;sGp0gOT43B PaJXxb0Ow{?;hRhVce4{$ delta 33 pcmZ3k)2FlHG!xTT)yZd>RxuvfT)|w%$9QD(dqFqG&3?i+nE>qo4O9RC diff --git a/DiscoveryEngine/metadata/V1/Control.php b/DiscoveryEngine/metadata/V1/Control.php index 940ff56533a75c63d8991a5f4abc0f1f7a48b563..d25197698c34beb86d7d0f7efeb474a00ced0da1 100644 GIT binary patch delta 51 zcmX>u_E2oYT_(m|lkYL@VZ1YWHuDN8Aqg(-!qU{D%J`zx^wbIo76nEPPABFEj*Od~ HSyHuH+ju`Dd?0E*5D?*IS* diff --git a/DiscoveryEngine/metadata/V1/SearchService.php b/DiscoveryEngine/metadata/V1/SearchService.php index 84d04cc513f5e80902e7c79f85ba2a7f46902dcb..b4d18f931ad410c7beb91cd97513a673cc5bc7b6 100644 GIT binary patch delta 125 zcmdnweQmLiie8vO@64V!~<0;6?Do`ZgK;c K+-6O+PDTJEWh)T? delta 38 ucmcZ-w8?+Nb7rRBZkt~+S1B-kW!{{svXGHgD(IA>+~fu>xy`55I~f5(zYiJ! diff --git a/DiscoveryEngine/samples/V1/SearchServiceClient/search_lite.php b/DiscoveryEngine/samples/V1/SearchServiceClient/search_lite.php new file mode 100644 index 000000000000..241b97ea5a71 --- /dev/null +++ b/DiscoveryEngine/samples/V1/SearchServiceClient/search_lite.php @@ -0,0 +1,98 @@ +setServingConfig($formattedServingConfig); + + // Call the API and handle any network failures. + try { + /** @var PagedListResponse $response */ + $response = $searchServiceClient->searchLite($request); + + /** @var SearchResult $element */ + foreach ($response as $element) { + printf('Element data: %s' . PHP_EOL, $element->serializeToJsonString()); + } + } catch (ApiException $ex) { + printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage()); + } +} + +/** + * Helper to execute the sample. + * + * This sample has been automatically generated and should be regarded as a code + * template only. It will require modifications to work: + * - It may require correct/in-range values for request initialization. + * - It may require specifying regional endpoints when creating the service client, + * please see the apiEndpoint client configuration option for more details. + */ +function callSample(): void +{ + $formattedServingConfig = SearchServiceClient::servingConfigName( + '[PROJECT]', + '[LOCATION]', + '[DATA_STORE]', + '[SERVING_CONFIG]' + ); + + search_lite_sample($formattedServingConfig); +} +// [END discoveryengine_v1_generated_SearchService_SearchLite_sync] diff --git a/DiscoveryEngine/src/V1/Answer/AnswerSkippedReason.php b/DiscoveryEngine/src/V1/Answer/AnswerSkippedReason.php index ef1da4f5f4bb..9f2365464bb2 100644 --- a/DiscoveryEngine/src/V1/Answer/AnswerSkippedReason.php +++ b/DiscoveryEngine/src/V1/Answer/AnswerSkippedReason.php @@ -79,6 +79,14 @@ class AnswerSkippedReason * Generated from protobuf enum NON_ANSWER_SEEKING_QUERY_IGNORED_V2 = 8; */ const NON_ANSWER_SEEKING_QUERY_IGNORED_V2 = 8; + /** + * The low-grounded answer case. + * Google skips the answer if a well grounded answer was unable to be + * generated. + * + * Generated from protobuf enum LOW_GROUNDED_ANSWER = 9; + */ + const LOW_GROUNDED_ANSWER = 9; private static $valueToName = [ self::ANSWER_SKIPPED_REASON_UNSPECIFIED => 'ANSWER_SKIPPED_REASON_UNSPECIFIED', @@ -90,6 +98,7 @@ class AnswerSkippedReason self::JAIL_BREAKING_QUERY_IGNORED => 'JAIL_BREAKING_QUERY_IGNORED', self::CUSTOMER_POLICY_VIOLATION => 'CUSTOMER_POLICY_VIOLATION', self::NON_ANSWER_SEEKING_QUERY_IGNORED_V2 => 'NON_ANSWER_SEEKING_QUERY_IGNORED_V2', + self::LOW_GROUNDED_ANSWER => 'LOW_GROUNDED_ANSWER', ]; public static function name($value) diff --git a/DiscoveryEngine/src/V1/BatchGetDocumentsMetadataResponse/DocumentMetadata/MatcherValue.php b/DiscoveryEngine/src/V1/BatchGetDocumentsMetadataResponse/DocumentMetadata/MatcherValue.php index 214dde886286..00319353ab3b 100644 --- a/DiscoveryEngine/src/V1/BatchGetDocumentsMetadataResponse/DocumentMetadata/MatcherValue.php +++ b/DiscoveryEngine/src/V1/BatchGetDocumentsMetadataResponse/DocumentMetadata/MatcherValue.php @@ -28,7 +28,7 @@ class MatcherValue extends \Google\Protobuf\Internal\Message * If match by URI, the URI of the * [Document][google.cloud.discoveryengine.v1.Document]. * @type string $fhir_resource - * Required. Format: + * Format: * projects/{project}/locations/{location}/datasets/{dataset}/fhirStores/{fhir_store}/fhir/{resource_type}/{fhir_resource_id} * } */ @@ -71,7 +71,7 @@ public function setUri($var) } /** - * Required. Format: + * Format: * projects/{project}/locations/{location}/datasets/{dataset}/fhirStores/{fhir_store}/fhir/{resource_type}/{fhir_resource_id} * * Generated from protobuf field string fhir_resource = 2 [(.google.api.resource_reference) = { @@ -88,7 +88,7 @@ public function hasFhirResource() } /** - * Required. Format: + * Format: * projects/{project}/locations/{location}/datasets/{dataset}/fhirStores/{fhir_store}/fhir/{resource_type}/{fhir_resource_id} * * Generated from protobuf field string fhir_resource = 2 [(.google.api.resource_reference) = { diff --git a/DiscoveryEngine/src/V1/Client/SearchServiceClient.php b/DiscoveryEngine/src/V1/Client/SearchServiceClient.php index d6dc62e8c140..e7c1a1e368e2 100644 --- a/DiscoveryEngine/src/V1/Client/SearchServiceClient.php +++ b/DiscoveryEngine/src/V1/Client/SearchServiceClient.php @@ -48,6 +48,7 @@ * contained within formatted names that are returned by the API. * * @method PromiseInterface searchAsync(SearchRequest $request, array $optionalArgs = []) + * @method PromiseInterface searchLiteAsync(SearchRequest $request, array $optionalArgs = []) */ final class SearchServiceClient { @@ -573,4 +574,42 @@ public function search(SearchRequest $request, array $callOptions = []): PagedLi { return $this->startApiCall('Search', $request, $callOptions); } + + /** + * Performs a search. Similar to the + * [SearchService.Search][google.cloud.discoveryengine.v1.SearchService.Search] + * method, but a lite version that allows API key for authentication, where + * OAuth and IAM checks are not required. + * + * Only public website search is supported by this method. If data stores and + * engines not associated with public website search are specified, a + * `FAILED_PRECONDITION` error is returned. + * + * This method can be used for easy onboarding without having to implement an + * authentication backend. However, it is strongly recommended to use + * [SearchService.Search][google.cloud.discoveryengine.v1.SearchService.Search] + * instead with required OAuth and IAM checks to provide better data security. + * + * The async variant is {@see SearchServiceClient::searchLiteAsync()} . + * + * @example samples/V1/SearchServiceClient/search_lite.php + * + * @param SearchRequest $request A request to house fields associated with the call. + * @param array $callOptions { + * Optional. + * + * @type RetrySettings|array $retrySettings + * Retry settings to use for this call. Can be a {@see RetrySettings} object, or an + * associative array of retry settings parameters. See the documentation on + * {@see RetrySettings} for example usage. + * } + * + * @return PagedListResponse + * + * @throws ApiException Thrown if the API call fails. + */ + public function searchLite(SearchRequest $request, array $callOptions = []): PagedListResponse + { + return $this->startApiCall('SearchLite', $request, $callOptions); + } } diff --git a/DiscoveryEngine/src/V1/Condition.php b/DiscoveryEngine/src/V1/Condition.php index acaf131b3572..0a122300ada0 100644 --- a/DiscoveryEngine/src/V1/Condition.php +++ b/DiscoveryEngine/src/V1/Condition.php @@ -18,6 +18,9 @@ class Condition extends \Google\Protobuf\Internal\Message /** * Search only * A list of terms to match the query on. + * Cannot be set when + * [Condition.query_regex][google.cloud.discoveryengine.v1.Condition.query_regex] + * is set. * Maximum of 10 query terms. * * Generated from protobuf field repeated .google.cloud.discoveryengine.v1.Condition.QueryTerm query_terms = 2; @@ -30,6 +33,15 @@ class Condition extends \Google\Protobuf\Internal\Message * Generated from protobuf field repeated .google.cloud.discoveryengine.v1.Condition.TimeRange active_time_range = 3; */ private $active_time_range; + /** + * Optional. Query regex to match the whole search query. + * Cannot be set when + * [Condition.query_terms][google.cloud.discoveryengine.v1.Condition.query_terms] + * is set. This is currently supporting promotion use case. + * + * Generated from protobuf field string query_regex = 4 [(.google.api.field_behavior) = OPTIONAL]; + */ + protected $query_regex = ''; /** * Constructor. @@ -40,10 +52,18 @@ class Condition extends \Google\Protobuf\Internal\Message * @type array<\Google\Cloud\DiscoveryEngine\V1\Condition\QueryTerm>|\Google\Protobuf\Internal\RepeatedField $query_terms * Search only * A list of terms to match the query on. + * Cannot be set when + * [Condition.query_regex][google.cloud.discoveryengine.v1.Condition.query_regex] + * is set. * Maximum of 10 query terms. * @type array<\Google\Cloud\DiscoveryEngine\V1\Condition\TimeRange>|\Google\Protobuf\Internal\RepeatedField $active_time_range * Range of time(s) specifying when condition is active. * Maximum of 10 time ranges. + * @type string $query_regex + * Optional. Query regex to match the whole search query. + * Cannot be set when + * [Condition.query_terms][google.cloud.discoveryengine.v1.Condition.query_terms] + * is set. This is currently supporting promotion use case. * } */ public function __construct($data = NULL) { @@ -54,6 +74,9 @@ public function __construct($data = NULL) { /** * Search only * A list of terms to match the query on. + * Cannot be set when + * [Condition.query_regex][google.cloud.discoveryengine.v1.Condition.query_regex] + * is set. * Maximum of 10 query terms. * * Generated from protobuf field repeated .google.cloud.discoveryengine.v1.Condition.QueryTerm query_terms = 2; @@ -67,6 +90,9 @@ public function getQueryTerms() /** * Search only * A list of terms to match the query on. + * Cannot be set when + * [Condition.query_regex][google.cloud.discoveryengine.v1.Condition.query_regex] + * is set. * Maximum of 10 query terms. * * Generated from protobuf field repeated .google.cloud.discoveryengine.v1.Condition.QueryTerm query_terms = 2; @@ -109,5 +135,37 @@ public function setActiveTimeRange($var) return $this; } + /** + * Optional. Query regex to match the whole search query. + * Cannot be set when + * [Condition.query_terms][google.cloud.discoveryengine.v1.Condition.query_terms] + * is set. This is currently supporting promotion use case. + * + * Generated from protobuf field string query_regex = 4 [(.google.api.field_behavior) = OPTIONAL]; + * @return string + */ + public function getQueryRegex() + { + return $this->query_regex; + } + + /** + * Optional. Query regex to match the whole search query. + * Cannot be set when + * [Condition.query_terms][google.cloud.discoveryengine.v1.Condition.query_terms] + * is set. This is currently supporting promotion use case. + * + * Generated from protobuf field string query_regex = 4 [(.google.api.field_behavior) = OPTIONAL]; + * @param string $var + * @return $this + */ + public function setQueryRegex($var) + { + GPBUtil::checkString($var, True); + $this->query_regex = $var; + + return $this; + } + } diff --git a/DiscoveryEngine/src/V1/gapic_metadata.json b/DiscoveryEngine/src/V1/gapic_metadata.json index b7befe39ec3f..22ca1b944631 100644 --- a/DiscoveryEngine/src/V1/gapic_metadata.json +++ b/DiscoveryEngine/src/V1/gapic_metadata.json @@ -82,6 +82,11 @@ "methods": [ "search" ] + }, + "SearchLite": { + "methods": [ + "searchLite" + ] } } } diff --git a/DiscoveryEngine/src/V1/resources/search_service_client_config.json b/DiscoveryEngine/src/V1/resources/search_service_client_config.json index 53151b5b8302..7b29799035ac 100644 --- a/DiscoveryEngine/src/V1/resources/search_service_client_config.json +++ b/DiscoveryEngine/src/V1/resources/search_service_client_config.json @@ -32,6 +32,11 @@ "timeout_millis": 30000, "retry_codes_name": "retry_policy_2_codes", "retry_params_name": "retry_policy_2_params" + }, + "SearchLite": { + "timeout_millis": 30000, + "retry_codes_name": "retry_policy_2_codes", + "retry_params_name": "retry_policy_2_params" } } } diff --git a/DiscoveryEngine/src/V1/resources/search_service_descriptor_config.php b/DiscoveryEngine/src/V1/resources/search_service_descriptor_config.php index 708f91e00e42..04927acc01a2 100644 --- a/DiscoveryEngine/src/V1/resources/search_service_descriptor_config.php +++ b/DiscoveryEngine/src/V1/resources/search_service_descriptor_config.php @@ -43,6 +43,26 @@ ], ], ], + 'SearchLite' => [ + 'pageStreaming' => [ + 'requestPageTokenGetMethod' => 'getPageToken', + 'requestPageTokenSetMethod' => 'setPageToken', + 'requestPageSizeGetMethod' => 'getPageSize', + 'requestPageSizeSetMethod' => 'setPageSize', + 'responsePageTokenGetMethod' => 'getNextPageToken', + 'resourcesGetMethod' => 'getResults', + ], + 'callType' => \Google\ApiCore\Call::PAGINATED_CALL, + 'responseType' => 'Google\Cloud\DiscoveryEngine\V1\SearchResponse', + 'headerParams' => [ + [ + 'keyName' => 'serving_config', + 'fieldAccessors' => [ + 'getServingConfig', + ], + ], + ], + ], 'templateMap' => [ 'branch' => 'projects/{project}/locations/{location}/dataStores/{data_store}/branches/{branch}', 'dataStore' => 'projects/{project}/locations/{location}/dataStores/{data_store}', diff --git a/DiscoveryEngine/src/V1/resources/search_service_rest_client_config.php b/DiscoveryEngine/src/V1/resources/search_service_rest_client_config.php index 61ff8f48813e..57bb2e58e3ff 100644 --- a/DiscoveryEngine/src/V1/resources/search_service_rest_client_config.php +++ b/DiscoveryEngine/src/V1/resources/search_service_rest_client_config.php @@ -47,6 +47,30 @@ ], ], ], + 'SearchLite' => [ + 'method' => 'post', + 'uriTemplate' => '/v1/{serving_config=projects/*/locations/*/dataStores/*/servingConfigs/*}:searchLite', + 'body' => '*', + 'additionalBindings' => [ + [ + 'method' => 'post', + 'uriTemplate' => '/v1/{serving_config=projects/*/locations/*/collections/*/dataStores/*/servingConfigs/*}:searchLite', + 'body' => '*', + ], + [ + 'method' => 'post', + 'uriTemplate' => '/v1/{serving_config=projects/*/locations/*/collections/*/engines/*/servingConfigs/*}:searchLite', + 'body' => '*', + ], + ], + 'placeholders' => [ + 'serving_config' => [ + 'getters' => [ + 'getServingConfig', + ], + ], + ], + ], ], 'google.longrunning.Operations' => [ 'CancelOperation' => [ diff --git a/DiscoveryEngine/tests/Unit/V1/Client/SearchServiceClientTest.php b/DiscoveryEngine/tests/Unit/V1/Client/SearchServiceClientTest.php index 72e0b116236d..4b14e94f092b 100644 --- a/DiscoveryEngine/tests/Unit/V1/Client/SearchServiceClientTest.php +++ b/DiscoveryEngine/tests/Unit/V1/Client/SearchServiceClientTest.php @@ -152,6 +152,95 @@ public function searchExceptionTest() $this->assertTrue($transport->isExhausted()); } + /** @test */ + public function searchLiteTest() + { + $transport = $this->createTransport(); + $gapicClient = $this->createClient([ + 'transport' => $transport, + ]); + $this->assertTrue($transport->isExhausted()); + // Mock response + $totalSize = 705419236; + $attributionToken = 'attributionToken-729411015'; + $redirectUri = 'redirectUri951230089'; + $nextPageToken = ''; + $correctedQuery = 'correctedQuery107869074'; + $resultsElement = new SearchResult(); + $results = [$resultsElement]; + $expectedResponse = new SearchResponse(); + $expectedResponse->setTotalSize($totalSize); + $expectedResponse->setAttributionToken($attributionToken); + $expectedResponse->setRedirectUri($redirectUri); + $expectedResponse->setNextPageToken($nextPageToken); + $expectedResponse->setCorrectedQuery($correctedQuery); + $expectedResponse->setResults($results); + $transport->addResponse($expectedResponse); + // Mock request + $formattedServingConfig = $gapicClient->servingConfigName( + '[PROJECT]', + '[LOCATION]', + '[DATA_STORE]', + '[SERVING_CONFIG]' + ); + $request = (new SearchRequest())->setServingConfig($formattedServingConfig); + $response = $gapicClient->searchLite($request); + $this->assertEquals($expectedResponse, $response->getPage()->getResponseObject()); + $resources = iterator_to_array($response->iterateAllElements()); + $this->assertSame(1, count($resources)); + $this->assertEquals($expectedResponse->getResults()[0], $resources[0]); + $actualRequests = $transport->popReceivedCalls(); + $this->assertSame(1, count($actualRequests)); + $actualFuncCall = $actualRequests[0]->getFuncCall(); + $actualRequestObject = $actualRequests[0]->getRequestObject(); + $this->assertSame('/google.cloud.discoveryengine.v1.SearchService/SearchLite', $actualFuncCall); + $actualValue = $actualRequestObject->getServingConfig(); + $this->assertProtobufEquals($formattedServingConfig, $actualValue); + $this->assertTrue($transport->isExhausted()); + } + + /** @test */ + public function searchLiteExceptionTest() + { + $transport = $this->createTransport(); + $gapicClient = $this->createClient([ + 'transport' => $transport, + ]); + $this->assertTrue($transport->isExhausted()); + $status = new stdClass(); + $status->code = Code::DATA_LOSS; + $status->details = 'internal error'; + $expectedExceptionMessage = json_encode( + [ + 'message' => 'internal error', + 'code' => Code::DATA_LOSS, + 'status' => 'DATA_LOSS', + 'details' => [], + ], + JSON_PRETTY_PRINT + ); + $transport->addResponse(null, $status); + // Mock request + $formattedServingConfig = $gapicClient->servingConfigName( + '[PROJECT]', + '[LOCATION]', + '[DATA_STORE]', + '[SERVING_CONFIG]' + ); + $request = (new SearchRequest())->setServingConfig($formattedServingConfig); + try { + $gapicClient->searchLite($request); + // If the $gapicClient method call did not throw, fail the test + $this->fail('Expected an ApiException, but no exception was thrown.'); + } catch (ApiException $ex) { + $this->assertEquals($status->code, $ex->getCode()); + $this->assertEquals($expectedExceptionMessage, $ex->getMessage()); + } + // Call popReceivedCalls to ensure the stub is exhausted + $transport->popReceivedCalls(); + $this->assertTrue($transport->isExhausted()); + } + /** @test */ public function searchAsyncTest() {