diff --git a/sdk/search/azure-search/CHANGELOG.md b/sdk/search/azure-search/CHANGELOG.md new file mode 100644 index 000000000000..fa10bc284d0e --- /dev/null +++ b/sdk/search/azure-search/CHANGELOG.md @@ -0,0 +1,3 @@ +# Release History + +## 11.0.0b1 (Unreleased) diff --git a/sdk/search/azure-search/LICENSE.txt b/sdk/search/azure-search/LICENSE.txt new file mode 100644 index 000000000000..0313a903d76c --- /dev/null +++ b/sdk/search/azure-search/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/search/azure-search/MANIFEST.in b/sdk/search/azure-search/MANIFEST.in new file mode 100644 index 000000000000..456471044cf1 --- /dev/null +++ b/sdk/search/azure-search/MANIFEST.in @@ -0,0 +1,5 @@ +include *.md +include azure/__init__.py +include LICENSE.txt +recursive-include tests *.py +recursive-include samples *.py *.md \ No newline at end of file diff --git a/sdk/search/azure-search/README.md b/sdk/search/azure-search/README.md new file mode 100644 index 000000000000..cf9a0e7ae5c1 --- /dev/null +++ b/sdk/search/azure-search/README.md @@ -0,0 +1,230 @@ +# Azure Cognitive Search client library for Python + +Azure Cognitive Search is a fully managed cloud search service that provides a rich search experience to custom applications. + +[Source code](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search) | +[Package (PyPI)](https://pypi.org/project/azure-search/) | +[API reference documentation](https://aka.ms/azsdk-python-search-ref-docs) | +[Product documentation](https://docs.microsoft.com/en-us/azure/search/search-what-is-azure-search) | +[Samples](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples) + +## Getting started + +### Prerequisites + +* Python 2.7, or 3.5 or later is required to use this package. +* You must have an [Azure subscription][azure_sub] and an existing. +[Azure Cognitive Search service][search_resource] to use this package. + +If you need to create the resource, you can use the [Azure Portal][azure_portal] or [Azure CLI][azure_cli]. + +If you use the Azure CLI, replace `` and `` with your own unique names: + +```PowerShell +az search service create --resource-group --name --sku S +``` + +The above creates a resource with the "Standard" pricing tier. See [choosing a pricing tier](https://docs.microsoft.com/en-us/azure/search/search-sku-tier) for more information. + + +### Install the package + +Install the Azure Cognitive Search client library for Python with [pip](https://pypi.org/project/pip/): + +```bash +pip install azure-search --pre +``` + +### Create an Azure Cognitive Search service + +### Using an API Key + +You can get the Query Keys or Admin Key from the resource information in the +[Azure Portal][azure_portal]. + +Alternatively, youcan se the [Azure CLI][azure_cli] snippet below to get the +Admin Key from the Cognitive Search resource. + +```PowerShell +az search admin-key show --resource-group --service-name +``` + +### Authenticate the client + +Interaction with this service begins with an instance of a [client](#client "search-client"). +To create a client object, you will need the `endpoint` for your search service +and a `credential` that allows you access: + +```python +from azure.search import SearchApiKeyCredential, SearchIndexClient + +credential = SearchApiKeyCredential("") + +client = SearchIndexClient(endpoint="", + index_name="", + credential=credential) +``` + +## Key concepts + +### Client + +The Cognitive Search client library provides a [SearchIndexClient](https://aka.ms/azsdk-python-search-searchindexclient) to perform search operations on [batches of documents](#Examples "examples"). +It provides both synchronous and asynchronous operations to access a specific use of Cognitive Search indexes, such as querying, suggestions or autocompletion. + + +## Examples + +### Retrieve a specific document from an index +Get a specific document from the index, e.f. obtain the document for hotel "23": +```python +from azure.search import SearchApiKeyCredential, SearchIndexClient +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +result = search_client.get_document(key="23") + +print("Details for hotel '23' are:") +print(" Name: {}".format(result["HotelName"])) +print(" Rating: {}".format(result["Rating"])) +print(" Category: {}".format(result["Category"])) +``` +### Perform a simple text search on documents +Search the entire index or documents matching a simple search text, e.g. find +hotels with the text "spa": +```python +from azure.search import SearchApiKeyCredential, SearchIndexClient +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +results = search_client.search(query="spa") + +print("Hotels containing 'spa' in the name (or other fields):") +for result in results: + print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) +``` +### Get search suggestions +Get search suggestions for related terms, e.g. find search suggestions for +the term "coffee": +```python +from azure.search import SearchApiKeyCredential, SearchIndexClient, SuggestQuery +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +query = SuggestQuery(search_text="coffee", suggester_name="sg") + +results = search_client.suggest(query=query) + +print("Search suggestions for 'coffee'") +for result in results: + hotel = search_client.get_document(key=result["HotelId"]) + print(" Text: {} for Hotel: {}".format(repr(result["text"]), hotel["HotelName"])) +``` +### Upload documents to an index +Add documents (or update existing ones), e.g add a new document for a new hotel: +```python +from azure.search import SearchApiKeyCredential, SearchIndexClient +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +DOCUMENT = { + 'Category': 'Hotel', + 'HotelId': '1000', + 'Rating': 4.0, + 'Rooms': [], + 'HotelName': 'Azure Inn', +} + +result = search_client.upload_documents(documents=[DOCUMENT]) + +print("Upload of new document succeeded: {}".format(result[0].succeeded)) +``` + +## Troubleshooting + +### General + +The Azure Cognitive Search client will raise exceptions defined in [Azure Core][azure_core]. + +### Logging + +This library uses the standard [logging][python_logging] library for logging. +Basic information about HTTP sessions (URLs, headers, etc.) is logged at INFO +level. + +etailed DEBUG level logging, including request/response bodies and unredacted +headers, can be enabled on a client with the `logging_enable` keyword argument: +```python +import sys +import logging +from azure.search import SearchApiKeyCredential, SearchIndexClient + +# Create a logger for the 'azure' SDK +logger = logging.getLogger('azure') +logger.setLevel(logging.DEBUG) + +# Configure a console output +handler = logging.StreamHandler(stream=sys.stdout) +logger.addHandler(handler) + +# This client will log detailed information about its HTTP sessions, at DEBUG level +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key), logging_enable=True) +``` + +Similarly, `logging_enable` can enable detailed logging for a single operation, +even when it isn't enabled for the client: +```python +result = search_client.search(query="spa", logging_enable=True) +``` + +## Next steps + +### More sample code + + +Authenticate the client with a Azure Cognitive Search [API Key Credential](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys): + +[sample_authentication.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_authentication.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/iasync_samples/sample_authentication_async.py)) + +Then for common search index operations: + +* Get a document by key: [sample_get_document.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_get_document.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_get_document_async.py)) + +* Perform a simple text query: [sample_simple_query.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_simple_query.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_simple_query_async.py)) + +* Perform a filtered query: [sample_filter_query.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_filter_query.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_filter_query_async.py)) + +* Get auto-completions: [sample_autocomplete.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_autocomplete.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_autocomplete_async.py)) + +* Get search suggestions: [sample_suggestions.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_suggestions.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_suggestions_async.py)) + +* Perform basic document updates: [sample_crud_operations.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_crud_operations.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_crud_operations_async.py)) + +### Additional documentation + +For more extensive documentation on Cognitive Search, see the [Azure Cognitive Search documentation](https://docs.microsoft.com/en-us/azure/search/) on docs.microsoft.com. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct][code_of_conduct]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. + +## Related projects + +* [Microsoft Azure SDK for Python](https://github.com/Azure/azure-sdk-for-python) + + + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fsdk%2Fsearch%2Fazure-search%2FREADME.png) + +[azure_cli]: https://docs.microsoft.com/cli/azure +[azure_core]: ../../core/azure-core/README.md +[azure_sub]: https://azure.microsoft.com/free/ +[search_resource]: https://docs.microsoft.com/en-us/azure/search/search-create-service-portal +[azure_portal]: https://portal.azure.com + +[python_logging]: https://docs.python.org/3.5/library/logging.html + +[cla]: https://cla.microsoft.com +[code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com diff --git a/sdk/search/azure-search/azure/__init__.py b/sdk/search/azure-search/azure/__init__.py new file mode 100644 index 000000000000..90d4cce39f79 --- /dev/null +++ b/sdk/search/azure-search/azure/__init__.py @@ -0,0 +1,6 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +# pylint:disable=missing-docstring +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: str diff --git a/sdk/search/azure-search/azure/search/__init__.py b/sdk/search/azure-search/azure/search/__init__.py new file mode 100644 index 000000000000..c1068bac6f52 --- /dev/null +++ b/sdk/search/azure-search/azure/search/__init__.py @@ -0,0 +1,53 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +from ._version import VERSION + +__version__ = VERSION + +from ._index import ( + AutocompleteQuery, + IndexAction, + IndexDocumentsBatch, + IndexingResult, + SearchApiKeyCredential, + SearchIndexClient, + SearchQuery, + SuggestQuery, + odata, +) + +__all__ = ( + "AutocompleteQuery", + "IndexAction", + "IndexDocumentsBatch", + "IndexingResult", + "SearchApiKeyCredential", + "SearchIndexClient", + "SearchQuery", + "SuggestQuery", + "odata", +) diff --git a/sdk/search/azure-search/azure/search/_index/__init__.py b/sdk/search/azure-search/azure/search/_index/__init__.py new file mode 100644 index 000000000000..9efc5ca23ab0 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/__init__.py @@ -0,0 +1,19 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from ._credential import SearchApiKeyCredential # pylint: disable=unused-import +from ._index_documents_batch import IndexDocumentsBatch # pylint: disable=unused-import +from ._search_index_client import ( # pylint: disable=unused-import + odata, + SearchIndexClient, +) +from ._queries import ( # pylint: disable=unused-import + AutocompleteQuery, + SearchQuery, + SuggestQuery, +) +from ._generated.models import ( # pylint: disable=unused-import + IndexAction, + IndexingResult, +) diff --git a/sdk/search/azure-search/azure/search/_index/_credential.py b/sdk/search/azure-search/azure/search/_index/_credential.py new file mode 100644 index 000000000000..708a2554b4fc --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_credential.py @@ -0,0 +1,41 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +import six + + +class SearchApiKeyCredential(object): + """Credential type used for authenticating a SearchIndexClient + with an admin or query API key. + + :param api_key: An admin or query key for your Azure Search index. + :type api_key: str + + """ + + def __init__(self, api_key): + # type: (str) -> None + if not isinstance(api_key, six.string_types): + raise TypeError("api_key must be a string.") + self._api_key = api_key # type: str + + @property + def api_key(self): + """The value of the configured API key. + + :rtype: str + """ + # type () -> str + return self._api_key + + def update_key(self, key): + """Update the API key. + This can be used when you've regenerated your service API key and want + to update long-lived clients. + + :param str key: The API key to your Azure search account. + """ + self._api_key = key diff --git a/sdk/search/azure-search/azure/search/_index/_generated/__init__.py b/sdk/search/azure-search/azure/search/_index/_generated/__init__.py new file mode 100644 index 000000000000..ac06514f5327 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._search_index_client import SearchIndexClient +__all__ = ['SearchIndexClient'] diff --git a/sdk/search/azure-search/azure/search/_index/_generated/_configuration.py b/sdk/search/azure-search/azure/search/_index/_generated/_configuration.py new file mode 100644 index 000000000000..3010c29cca9f --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/_configuration.py @@ -0,0 +1,57 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class SearchIndexClientConfiguration(Configuration): + """Configuration for SearchIndexClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The endpoint URL of the search service. + :type endpoint: str + :param index_name: The name of the index. + :type index_name: str + """ + + def __init__( + self, + endpoint, # type: str + index_name, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + if index_name is None: + raise ValueError("Parameter 'index_name' must not be None.") + super(SearchIndexClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.index_name = index_name + self.api_version = "2019-05-06-Preview" + kwargs.setdefault('sdk_moniker', 'searchindexclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/search/azure-search/azure/search/_index/_generated/_search_index_client.py b/sdk/search/azure-search/azure/search/_index/_generated/_search_index_client.py new file mode 100644 index 000000000000..7f112a9888de --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/_search_index_client.py @@ -0,0 +1,57 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core import PipelineClient +from msrest import Deserializer, Serializer + +from ._configuration import SearchIndexClientConfiguration +from .operations import DocumentsOperations +from . import models + + +class SearchIndexClient(object): + """Client that can be used to query an index and upload, merge, or delete documents. + + :ivar documents: DocumentsOperations operations + :vartype documents: search_index_client.operations.DocumentsOperations + :param endpoint: The endpoint URL of the search service. + :type endpoint: str + :param index_name: The name of the index. + :type index_name: str + """ + + def __init__( + self, + endpoint, # type: str + index_name, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + base_url = '{endpoint}/indexes(\'{indexName}\')' + self._config = SearchIndexClientConfiguration(endpoint, index_name, **kwargs) + self._client = PipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.documents = DocumentsOperations( + self._client, self._config, self._serialize, self._deserialize) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> SearchIndexClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/sdk/search/azure-search/azure/search/_index/_generated/aio/__init__.py b/sdk/search/azure-search/azure/search/_index/_generated/aio/__init__.py new file mode 100644 index 000000000000..a06ffca12355 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/aio/__init__.py @@ -0,0 +1,8 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._search_index_client_async import SearchIndexClient +__all__ = ['SearchIndexClient'] diff --git a/sdk/search/azure-search/azure/search/_index/_generated/aio/_configuration_async.py b/sdk/search/azure-search/azure/search/_index/_generated/aio/_configuration_async.py new file mode 100644 index 000000000000..2fe06bf7f544 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/aio/_configuration_async.py @@ -0,0 +1,55 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies + +VERSION = "unknown" + +class SearchIndexClientConfiguration(Configuration): + """Configuration for SearchIndexClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: The endpoint URL of the search service. + :type endpoint: str + :param index_name: The name of the index. + :type index_name: str + """ + + def __init__( + self, + endpoint: str, + index_name: str, + **kwargs: Any + ) -> None: + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + if index_name is None: + raise ValueError("Parameter 'index_name' must not be None.") + super(SearchIndexClientConfiguration, self).__init__(**kwargs) + + self.endpoint = endpoint + self.index_name = index_name + self.api_version = "2019-05-06-Preview" + kwargs.setdefault('sdk_moniker', 'searchindexclient/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') diff --git a/sdk/search/azure-search/azure/search/_index/_generated/aio/_search_index_client_async.py b/sdk/search/azure-search/azure/search/_index/_generated/aio/_search_index_client_async.py new file mode 100644 index 000000000000..06ec78b349c8 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/aio/_search_index_client_async.py @@ -0,0 +1,53 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any + +from azure.core import AsyncPipelineClient +from msrest import Deserializer, Serializer + +from ._configuration_async import SearchIndexClientConfiguration +from .operations_async import DocumentsOperations +from .. import models + + +class SearchIndexClient(object): + """Client that can be used to query an index and upload, merge, or delete documents. + + :ivar documents: DocumentsOperations operations + :vartype documents: search_index_client.aio.operations_async.DocumentsOperations + :param endpoint: The endpoint URL of the search service. + :type endpoint: str + :param index_name: The name of the index. + :type index_name: str + """ + + def __init__( + self, + endpoint: str, + index_name: str, + **kwargs: Any + ) -> None: + base_url = '{endpoint}/indexes(\'{indexName}\')' + self._config = SearchIndexClientConfiguration(endpoint, index_name, **kwargs) + self._client = AsyncPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.documents = DocumentsOperations( + self._client, self._config, self._serialize, self._deserialize) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "SearchIndexClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/__init__.py b/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/__init__.py new file mode 100644 index 000000000000..6b51d112c132 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/__init__.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._documents_operations_async import DocumentsOperations + +__all__ = [ + 'DocumentsOperations', +] diff --git a/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/_documents_operations_async.py b/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/_documents_operations_async.py new file mode 100644 index 000000000000..dc288eb01147 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/aio/operations_async/_documents_operations_async.py @@ -0,0 +1,767 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union +import warnings + +from azure.core.exceptions import map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest + +from ... import models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class DocumentsOperations: + """DocumentsOperations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~search_index_client.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + async def count( + self, + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> int: + """Queries the number of documents in the index. + + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: long or the result of cls(response) + :rtype: long + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[int] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.count.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('long', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + count.metadata = {'url': '/docs/$count'} + + async def search_get( + self, + search_text: Optional[str] = None, + search_options: Optional["models.SearchOptions"] = None, + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.SearchDocumentsResult": + """Searches for documents in the index. + + :param search_text: A full-text search query expression; Use "*" or omit this parameter to + match all documents. + :type search_text: str + :param search_options: Parameter group. + :type search_options: ~search_index_client.models.SearchOptions + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SearchDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SearchDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SearchDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _include_total_result_count = None + _facets = None + _filter = None + _highlight_fields = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _order_by = None + _query_type = None + _scoring_parameters = None + _scoring_profile = None + _search_fields = None + _search_mode = None + _select = None + _skip = None + _top = None + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + if search_options is not None: + _include_total_result_count = search_options.include_total_result_count + _facets = search_options.facets + _filter = search_options.filter + _highlight_fields = search_options.highlight_fields + _highlight_post_tag = search_options.highlight_post_tag + _highlight_pre_tag = search_options.highlight_pre_tag + _minimum_coverage = search_options.minimum_coverage + _order_by = search_options.order_by + _query_type = search_options.query_type + _scoring_parameters = search_options.scoring_parameters + _scoring_profile = search_options.scoring_profile + _search_fields = search_options.search_fields + _search_mode = search_options.search_mode + _select = search_options.select + _skip = search_options.skip + _top = search_options.top + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.search_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if search_text is not None: + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + if _include_total_result_count is not None: + query_parameters['$count'] = self._serialize.query("include_total_result_count", _include_total_result_count, 'bool') + if _facets is not None: + query_parameters['facet'] = self._serialize.query("facets", _facets, '[str]', div=',') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _highlight_fields is not None: + query_parameters['highlight'] = self._serialize.query("highlight_fields", _highlight_fields, '[str]') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _order_by is not None: + query_parameters['$orderby'] = self._serialize.query("order_by", _order_by, '[str]') + if _query_type is not None: + query_parameters['queryType'] = self._serialize.query("query_type", _query_type, 'str') + if _scoring_parameters is not None: + query_parameters['scoringParameter'] = self._serialize.query("scoring_parameters", _scoring_parameters, '[str]', div=',') + if _scoring_profile is not None: + query_parameters['scoringProfile'] = self._serialize.query("scoring_profile", _scoring_profile, 'str') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _search_mode is not None: + query_parameters['searchMode'] = self._serialize.query("search_mode", _search_mode, 'str') + if _select is not None: + query_parameters['$select'] = self._serialize.query("select", _select, '[str]') + if _skip is not None: + query_parameters['$skip'] = self._serialize.query("skip", _skip, 'int') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SearchDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + search_get.metadata = {'url': '/docs'} + + async def search_post( + self, + search_request: "models.SearchRequest", + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.SearchDocumentsResult": + """Searches for documents in the index. + + :param search_request: The definition of the Search request. + :type search_request: ~search_index_client.models.SearchRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SearchDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SearchDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SearchDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.search_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(search_request, 'SearchRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SearchDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + search_post.metadata = {'url': '/docs/search.post.search'} + + async def get( + self, + key: str, + selected_fields: Optional[List[str]] = None, + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> object: + """Retrieves a document from the index. + + :param key: The key of the document to retrieve. + :type key: str + :param selected_fields: List of field names to retrieve for the document; Any field not + retrieved will be missing from the returned document. + :type selected_fields: list[str] + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: object or the result of cls(response) + :rtype: object + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[object] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + 'key': self._serialize.url("key", key, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if selected_fields is not None: + query_parameters['$select'] = self._serialize.query("selected_fields", selected_fields, '[str]') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('object', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get.metadata = {'url': '/docs(\'{key}\')'} + + async def suggest_get( + self, + search_text: str, + suggester_name: str, + suggest_options: Optional["models.SuggestOptions"] = None, + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.SuggestDocumentsResult": + """Suggests documents in the index that match the given partial query text. + + :param search_text: The search text to use to suggest documents. Must be at least 1 character, + and no more than 100 characters. + :type search_text: str + :param suggester_name: The name of the suggester as specified in the suggesters collection + that's part of the index definition. + :type suggester_name: str + :param suggest_options: Parameter group. + :type suggest_options: ~search_index_client.models.SuggestOptions + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SuggestDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SuggestDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SuggestDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _filter = None + _use_fuzzy_matching = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _order_by = None + _search_fields = None + _select = None + _top = None + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + if suggest_options is not None: + _filter = suggest_options.filter + _use_fuzzy_matching = suggest_options.use_fuzzy_matching + _highlight_post_tag = suggest_options.highlight_post_tag + _highlight_pre_tag = suggest_options.highlight_pre_tag + _minimum_coverage = suggest_options.minimum_coverage + _order_by = suggest_options.order_by + _search_fields = suggest_options.search_fields + _select = suggest_options.select + _top = suggest_options.top + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.suggest_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + query_parameters['suggesterName'] = self._serialize.query("suggester_name", suggester_name, 'str') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _use_fuzzy_matching is not None: + query_parameters['fuzzy'] = self._serialize.query("use_fuzzy_matching", _use_fuzzy_matching, 'bool') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _order_by is not None: + query_parameters['$orderby'] = self._serialize.query("order_by", _order_by, '[str]') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _select is not None: + query_parameters['$select'] = self._serialize.query("select", _select, '[str]') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SuggestDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + suggest_get.metadata = {'url': '/docs/search.suggest'} + + async def suggest_post( + self, + suggest_request: "models.SuggestRequest", + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.SuggestDocumentsResult": + """Suggests documents in the index that match the given partial query text. + + :param suggest_request: The Suggest request. + :type suggest_request: ~search_index_client.models.SuggestRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SuggestDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SuggestDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SuggestDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.suggest_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(suggest_request, 'SuggestRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SuggestDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + suggest_post.metadata = {'url': '/docs/search.post.suggest'} + + async def index( + self, + batch: "models.IndexBatch", + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.IndexDocumentsResult": + """Sends a batch of document write actions to the index. + + :param batch: The batch of index actions. + :type batch: ~search_index_client.models.IndexBatch + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: IndexDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.IndexDocumentsResult or ~search_index_client.models.IndexDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.IndexDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.index.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(batch, 'IndexBatch') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 207]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = None + if response.status_code == 200: + deserialized = self._deserialize('IndexDocumentsResult', pipeline_response) + + if response.status_code == 207: + deserialized = self._deserialize('IndexDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + index.metadata = {'url': '/docs/search.index'} + + async def autocomplete_get( + self, + search_text: str, + suggester_name: str, + request_options: Optional["models.RequestOptions"] = None, + autocomplete_options: Optional["models.AutocompleteOptions"] = None, + **kwargs + ) -> "models.AutocompleteResult": + """Autocompletes incomplete query terms based on input text and matching terms in the index. + + :param search_text: The incomplete term which should be auto-completed. + :type search_text: str + :param suggester_name: The name of the suggester as specified in the suggesters collection + that's part of the index definition. + :type suggester_name: str + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :param autocomplete_options: Parameter group. + :type autocomplete_options: ~search_index_client.models.AutocompleteOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: AutocompleteResult or the result of cls(response) + :rtype: ~search_index_client.models.AutocompleteResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.AutocompleteResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + _autocomplete_mode = None + _filter = None + _use_fuzzy_matching = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _search_fields = None + _top = None + if autocomplete_options is not None: + _autocomplete_mode = autocomplete_options.autocomplete_mode + _filter = autocomplete_options.filter + _use_fuzzy_matching = autocomplete_options.use_fuzzy_matching + _highlight_post_tag = autocomplete_options.highlight_post_tag + _highlight_pre_tag = autocomplete_options.highlight_pre_tag + _minimum_coverage = autocomplete_options.minimum_coverage + _search_fields = autocomplete_options.search_fields + _top = autocomplete_options.top + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.autocomplete_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + query_parameters['suggesterName'] = self._serialize.query("suggester_name", suggester_name, 'str') + if _autocomplete_mode is not None: + query_parameters['autocompleteMode'] = self._serialize.query("autocomplete_mode", _autocomplete_mode, 'str') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _use_fuzzy_matching is not None: + query_parameters['fuzzy'] = self._serialize.query("use_fuzzy_matching", _use_fuzzy_matching, 'bool') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('AutocompleteResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + autocomplete_get.metadata = {'url': '/docs/search.autocomplete'} + + async def autocomplete_post( + self, + autocomplete_request: "models.AutocompleteRequest", + request_options: Optional["models.RequestOptions"] = None, + **kwargs + ) -> "models.AutocompleteResult": + """Autocompletes incomplete query terms based on input text and matching terms in the index. + + :param autocomplete_request: The definition of the Autocomplete request. + :type autocomplete_request: ~search_index_client.models.AutocompleteRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: AutocompleteResult or the result of cls(response) + :rtype: ~search_index_client.models.AutocompleteResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.AutocompleteResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.autocomplete_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(autocomplete_request, 'AutocompleteRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('AutocompleteResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + autocomplete_post.metadata = {'url': '/docs/search.post.autocomplete'} diff --git a/sdk/search/azure-search/azure/search/_index/_generated/models/__init__.py b/sdk/search/azure-search/azure/search/_index/_generated/models/__init__.py new file mode 100644 index 000000000000..ad760a1d771e --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/models/__init__.py @@ -0,0 +1,79 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import AutocompleteItem + from ._models_py3 import AutocompleteOptions + from ._models_py3 import AutocompleteRequest + from ._models_py3 import AutocompleteResult + from ._models_py3 import FacetResult + from ._models_py3 import IndexAction + from ._models_py3 import IndexBatch + from ._models_py3 import IndexDocumentsResult + from ._models_py3 import IndexingResult + from ._models_py3 import RequestOptions + from ._models_py3 import SearchDocumentsResult + from ._models_py3 import SearchError, SearchErrorException + from ._models_py3 import SearchOptions + from ._models_py3 import SearchRequest + from ._models_py3 import SearchResult + from ._models_py3 import SuggestDocumentsResult + from ._models_py3 import SuggestOptions + from ._models_py3 import SuggestRequest + from ._models_py3 import SuggestResult +except (SyntaxError, ImportError): + from ._models import AutocompleteItem # type: ignore + from ._models import AutocompleteOptions # type: ignore + from ._models import AutocompleteRequest # type: ignore + from ._models import AutocompleteResult # type: ignore + from ._models import FacetResult # type: ignore + from ._models import IndexAction # type: ignore + from ._models import IndexBatch # type: ignore + from ._models import IndexDocumentsResult # type: ignore + from ._models import IndexingResult # type: ignore + from ._models import RequestOptions # type: ignore + from ._models import SearchDocumentsResult # type: ignore + from ._models import SearchError, SearchErrorException # type: ignore + from ._models import SearchOptions # type: ignore + from ._models import SearchRequest # type: ignore + from ._models import SearchResult # type: ignore + from ._models import SuggestDocumentsResult # type: ignore + from ._models import SuggestOptions # type: ignore + from ._models import SuggestRequest # type: ignore + from ._models import SuggestResult # type: ignore + +from ._search_index_client_enums import ( + AutocompleteMode, + IndexActionType, + QueryType, + SearchMode, +) + +__all__ = [ + 'AutocompleteItem', + 'AutocompleteOptions', + 'AutocompleteRequest', + 'AutocompleteResult', + 'FacetResult', + 'IndexAction', + 'IndexBatch', + 'IndexDocumentsResult', + 'IndexingResult', + 'RequestOptions', + 'SearchDocumentsResult', + 'SearchError', 'SearchErrorException', + 'SearchOptions', + 'SearchRequest', + 'SearchResult', + 'SuggestDocumentsResult', + 'SuggestOptions', + 'SuggestRequest', + 'SuggestResult', + 'AutocompleteMode', + 'IndexActionType', + 'QueryType', + 'SearchMode', +] diff --git a/sdk/search/azure-search/azure/search/_index/_generated/models/_models.py b/sdk/search/azure-search/azure/search/_index/_generated/models/_models.py new file mode 100644 index 000000000000..88b4aacde572 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/models/_models.py @@ -0,0 +1,993 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class AutocompleteItem(msrest.serialization.Model): + """The result of Autocomplete requests. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar text: Required. The completed term. + :vartype text: str + :ivar query_plus_text: Required. The query along with the completed term. + :vartype query_plus_text: str + """ + + _validation = { + 'text': {'required': True, 'readonly': True}, + 'query_plus_text': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'text': {'key': 'text', 'type': 'str'}, + 'query_plus_text': {'key': 'queryPlusText', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteItem, self).__init__(**kwargs) + self.text = None + self.query_plus_text = None + + +class AutocompleteOptions(msrest.serialization.Model): + """Parameter group. + + :param autocomplete_mode: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use + 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing + auto-completed terms. Possible values include: 'oneTerm', 'twoTerms', 'oneTermWithContext'. + :type autocomplete_mode: str or ~search_index_client.models.AutocompleteMode + :param filter: An OData expression that filters the documents used to produce completed terms + for the Autocomplete result. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the + autocomplete query. Default is false. When set to true, the query will find terms even if + there's a substituted or missing character in the search text. While this provides a better + experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are + slower and consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by an autocomplete query in order for the query to be reported as a success. + This parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param search_fields: The list of field names to consider when querying for auto-completed + terms. Target fields must be included in the specified suggester. + :type search_fields: list[str] + :param top: The number of auto-completed terms to retrieve. This must be a value between 1 and + 100. The default is 5. + :type top: int + """ + + _attribute_map = { + 'autocomplete_mode': {'key': 'autocompleteMode', 'type': 'str'}, + 'filter': {'key': '$filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'UseFuzzyMatching', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteOptions, self).__init__(**kwargs) + self.autocomplete_mode = kwargs.get('autocomplete_mode', None) + self.filter = kwargs.get('filter', None) + self.use_fuzzy_matching = kwargs.get('use_fuzzy_matching', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.search_fields = kwargs.get('search_fields', None) + self.top = kwargs.get('top', None) + + +class AutocompleteRequest(msrest.serialization.Model): + """Parameters for fuzzy matching, and other autocomplete query behaviors. + + All required parameters must be populated in order to send to Azure. + + :param search_text: Required. The search text on which to base autocomplete results. + :type search_text: str + :param autocomplete_mode: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use + 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing + auto-completed terms. Possible values include: 'oneTerm', 'twoTerms', 'oneTermWithContext'. + :type autocomplete_mode: str or ~search_index_client.models.AutocompleteMode + :param filter: An OData expression that filters the documents used to produce completed terms + for the Autocomplete result. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the + autocomplete query. Default is false. When set to true, the query will autocomplete terms even + if there's a substituted or missing character in the search text. While this provides a better + experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are + slower and consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by an autocomplete query in order for the query to be reported as a success. + This parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param search_fields: The comma-separated list of field names to consider when querying for + auto-completed terms. Target fields must be included in the specified suggester. + :type search_fields: str + :param suggester_name: Required. The name of the suggester as specified in the suggesters + collection that's part of the index definition. + :type suggester_name: str + :param top: The number of auto-completed terms to retrieve. This must be a value between 1 and + 100. The default is 5. + :type top: int + """ + + _validation = { + 'search_text': {'required': True}, + 'suggester_name': {'required': True}, + } + + _attribute_map = { + 'search_text': {'key': 'search', 'type': 'str'}, + 'autocomplete_mode': {'key': 'autocompleteMode', 'type': 'str'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'fuzzy', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'suggester_name': {'key': 'suggesterName', 'type': 'str'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteRequest, self).__init__(**kwargs) + self.search_text = kwargs.get('search_text', None) + self.autocomplete_mode = kwargs.get('autocomplete_mode', None) + self.filter = kwargs.get('filter', None) + self.use_fuzzy_matching = kwargs.get('use_fuzzy_matching', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.search_fields = kwargs.get('search_fields', None) + self.suggester_name = kwargs.get('suggester_name', None) + self.top = kwargs.get('top', None) + + +class AutocompleteResult(msrest.serialization.Model): + """The result of Autocomplete query. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar coverage: A value indicating the percentage of the index that was considered by the + autocomplete request, or null if minimumCoverage was not specified in the request. + :vartype coverage: float + :ivar results: Required. The list of returned Autocompleted items. + :vartype results: list[~search_index_client.models.AutocompleteItem] + """ + + _validation = { + 'coverage': {'readonly': True}, + 'results': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + 'results': {'key': 'value', 'type': '[AutocompleteItem]'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteResult, self).__init__(**kwargs) + self.coverage = None + self.results = None + + +class FacetResult(msrest.serialization.Model): + """A single bucket of a facet query result. Reports the number of documents with a field value falling within a particular range or having a particular value or interval. + + Variables are only populated by the server, and will be ignored when sending a request. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar count: The approximate count of documents falling within the bucket described by this + facet. + :vartype count: long + """ + + _validation = { + 'count': {'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'count': {'key': 'count', 'type': 'long'}, + } + + def __init__( + self, + **kwargs + ): + super(FacetResult, self).__init__(**kwargs) + self.additional_properties = kwargs.get('additional_properties', None) + self.count = None + + +class IndexAction(msrest.serialization.Model): + """Represents an index action that operates on a document. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :param action_type: The operation to perform on a document in an indexing batch. Possible + values include: 'upload', 'merge', 'mergeOrUpload', 'delete'. + :type action_type: str or ~search_index_client.models.IndexActionType + """ + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'action_type': {'key': '@search\\.action', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexAction, self).__init__(**kwargs) + self.additional_properties = kwargs.get('additional_properties', None) + self.action_type = kwargs.get('action_type', None) + + +class IndexBatch(msrest.serialization.Model): + """Contains a batch of document write actions to send to the index. + + All required parameters must be populated in order to send to Azure. + + :param actions: Required. The actions in the batch. + :type actions: list[~search_index_client.models.IndexAction] + """ + + _validation = { + 'actions': {'required': True}, + } + + _attribute_map = { + 'actions': {'key': 'value', 'type': '[IndexAction]'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexBatch, self).__init__(**kwargs) + self.actions = kwargs.get('actions', None) + + +class IndexDocumentsResult(msrest.serialization.Model): + """Response containing the status of operations for all documents in the indexing request. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar results: Required. The list of status information for each document in the indexing + request. + :vartype results: list[~search_index_client.models.IndexingResult] + """ + + _validation = { + 'results': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'results': {'key': 'value', 'type': '[IndexingResult]'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexDocumentsResult, self).__init__(**kwargs) + self.results = None + + +class IndexingResult(msrest.serialization.Model): + """Status of an indexing operation for a single document. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar key: Required. The key of a document that was in the indexing request. + :vartype key: str + :ivar error_message: The error message explaining why the indexing operation failed for the + document identified by the key; null if indexing succeeded. + :vartype error_message: str + :ivar succeeded: Required. A value indicating whether the indexing operation succeeded for the + document identified by the key. + :vartype succeeded: bool + :ivar status_code: Required. The status code of the indexing operation. Possible values + include: 200 for a successful update or delete, 201 for successful document creation, 400 for a + malformed input document, 404 for document not found, 409 for a version conflict, 422 when the + index is temporarily unavailable, or 503 for when the service is too busy. + :vartype status_code: int + """ + + _validation = { + 'key': {'required': True, 'readonly': True}, + 'error_message': {'readonly': True}, + 'succeeded': {'required': True, 'readonly': True}, + 'status_code': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'key': {'key': 'key', 'type': 'str'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + 'succeeded': {'key': 'status', 'type': 'bool'}, + 'status_code': {'key': 'statusCode', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexingResult, self).__init__(**kwargs) + self.key = None + self.error_message = None + self.succeeded = None + self.status_code = None + + +class RequestOptions(msrest.serialization.Model): + """Parameter group. + + :param x_ms_client_request_id: The tracking ID sent with the request to help with debugging. + :type x_ms_client_request_id: str + """ + + _attribute_map = { + 'x_ms_client_request_id': {'key': 'x-ms-client-request-id', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(RequestOptions, self).__init__(**kwargs) + self.x_ms_client_request_id = kwargs.get('x_ms_client_request_id', None) + + +class SearchDocumentsResult(msrest.serialization.Model): + """Response containing search results from an index. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar count: The total count of results found by the search operation, or null if the count was + not requested. If present, the count may be greater than the number of results in this + response. This can happen if you use the $top or $skip parameters, or if Azure Cognitive Search + can't return all the requested documents in a single Search response. + :vartype count: long + :ivar coverage: A value indicating the percentage of the index that was included in the query, + or null if minimumCoverage was not specified in the request. + :vartype coverage: float + :ivar facets: The facet query results for the search operation, organized as a collection of + buckets for each faceted field; null if the query did not include any facet expressions. + :vartype facets: dict[str, list[~search_index_client.models.FacetResult]] + :ivar next_page_parameters: Continuation JSON payload returned when Azure Cognitive Search + can't return all the requested results in a single Search response. You can use this JSON along + with @odata.nextLink to formulate another POST Search request to get the next part of the + search response. + :vartype next_page_parameters: ~search_index_client.models.SearchRequest + :ivar results: Required. The sequence of results returned by the query. + :vartype results: list[~search_index_client.models.SearchResult] + :ivar next_link: Continuation URL returned when Azure Cognitive Search can't return all the + requested results in a single Search response. You can use this URL to formulate another GET or + POST Search request to get the next part of the search response. Make sure to use the same verb + (GET or POST) as the request that produced this response. + :vartype next_link: str + """ + + _validation = { + 'count': {'readonly': True}, + 'coverage': {'readonly': True}, + 'facets': {'readonly': True}, + 'next_page_parameters': {'readonly': True}, + 'results': {'required': True, 'readonly': True}, + 'next_link': {'readonly': True}, + } + + _attribute_map = { + 'count': {'key': '@odata\\.count', 'type': 'long'}, + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + 'facets': {'key': '@search\\.facets', 'type': '{[FacetResult]}'}, + 'next_page_parameters': {'key': '@search\\.nextPageParameters', 'type': 'SearchRequest'}, + 'results': {'key': 'value', 'type': '[SearchResult]'}, + 'next_link': {'key': '@odata\\.nextLink', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchDocumentsResult, self).__init__(**kwargs) + self.count = None + self.coverage = None + self.facets = None + self.next_page_parameters = None + self.results = None + self.next_link = None + + +class SearchErrorException(HttpResponseError): + """Server responded with exception of type: 'SearchError'. + + :param response: Server response to be deserialized. + :param error_model: A deserialized model of the response body as model. + """ + + def __init__(self, response, error_model): + self.error = error_model + super(SearchErrorException, self).__init__(response=response, error_model=error_model) + + @classmethod + def from_response(cls, response, deserialize): + """Deserialize this response as this exception, or a subclass of this exception. + + :param response: Server response to be deserialized. + :param deserialize: A deserializer + """ + model_name = 'SearchError' + error = deserialize(model_name, response) + if error is None: + error = deserialize.dependencies[model_name]() + return error._EXCEPTION_TYPE(response, error) + + +class SearchError(msrest.serialization.Model): + """Describes an error condition for the Azure Cognitive Search API. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: One of a server-defined set of error codes. + :vartype code: str + :ivar message: Required. A human-readable representation of the error. + :vartype message: str + :ivar details: An array of details about specific errors that led to this reported error. + :vartype details: list[~search_index_client.models.SearchError] + """ + _EXCEPTION_TYPE = SearchErrorException + + _validation = { + 'code': {'readonly': True}, + 'message': {'required': True, 'readonly': True}, + 'details': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[SearchError]'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchError, self).__init__(**kwargs) + self.code = None + self.message = None + self.details = None + + +class SearchOptions(msrest.serialization.Model): + """Parameter group. + + :param include_total_result_count: A value that specifies whether to fetch the total count of + results. Default is false. Setting this value to true may have a performance impact. Note that + the count returned is an approximation. + :type include_total_result_count: bool + :param facets: The list of facet expressions to apply to the search query. Each facet + expression contains a field name, optionally followed by a comma-separated list of name:value + pairs. + :type facets: list[str] + :param filter: The OData $filter expression to apply to the search query. + :type filter: str + :param highlight_fields: The list of field names to use for hit highlights. Only searchable + fields can be used for hit highlighting. + :type highlight_fields: list[str] + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. Default is </em>. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. Default is <em>. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a search query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 100. + :type minimum_coverage: float + :param order_by: The list of OData $orderby expressions by which to sort the results. Each + expression can be either a field name or a call to either the geo.distance() or the + search.score() functions. Each expression can be followed by asc to indicate ascending, and + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no OrderBy is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: list[str] + :param query_type: A value that specifies the syntax of the search query. The default is + 'simple'. Use 'full' if your query uses the Lucene query syntax. Possible values include: + 'simple', 'full'. + :type query_type: str or ~search_index_client.models.QueryType + :param scoring_parameters: The list of parameter values to be used in scoring functions (for + example, referencePointParameter) using the format name-values. For example, if the scoring + profile defines a function with a parameter called 'mylocation' the parameter string would be + "mylocation--122.2,44.8" (without the quotes). + :type scoring_parameters: list[str] + :param scoring_profile: The name of a scoring profile to evaluate match scores for matching + documents in order to sort the results. + :type scoring_profile: str + :param search_fields: The list of field names to which to scope the full-text search. When + using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of + each fielded search expression take precedence over any field names listed in this parameter. + :type search_fields: list[str] + :param search_mode: A value that specifies whether any or all of the search terms must be + matched in order to count the document as a match. Possible values include: 'any', 'all'. + :type search_mode: str or ~search_index_client.models.SearchMode + :param select: The list of fields to retrieve. If unspecified, all fields marked as retrievable + in the schema are included. + :type select: list[str] + :param skip: The number of search results to skip. This value cannot be greater than 100,000. + If you need to scan documents in sequence, but cannot use $skip due to this limitation, + consider using $orderby on a totally-ordered key and $filter with a range query instead. + :type skip: int + :param top: The number of search results to retrieve. This can be used in conjunction with + $skip to implement client-side paging of search results. If results are truncated due to + server-side paging, the response will include a continuation token that can be used to issue + another Search request for the next page of results. + :type top: int + """ + + _attribute_map = { + 'include_total_result_count': {'key': 'IncludeTotalResultCount', 'type': 'bool'}, + 'facets': {'key': 'Facets', 'type': '[str]'}, + 'filter': {'key': '$filter', 'type': 'str'}, + 'highlight_fields': {'key': 'HighlightFields', 'type': '[str]'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'OrderBy', 'type': '[str]'}, + 'query_type': {'key': 'queryType', 'type': 'str'}, + 'scoring_parameters': {'key': 'ScoringParameters', 'type': '[str]'}, + 'scoring_profile': {'key': 'scoringProfile', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'search_mode': {'key': 'searchMode', 'type': 'str'}, + 'select': {'key': '$select', 'type': '[str]'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchOptions, self).__init__(**kwargs) + self.include_total_result_count = kwargs.get('include_total_result_count', None) + self.facets = kwargs.get('facets', None) + self.filter = kwargs.get('filter', None) + self.highlight_fields = kwargs.get('highlight_fields', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.order_by = kwargs.get('order_by', None) + self.query_type = kwargs.get('query_type', None) + self.scoring_parameters = kwargs.get('scoring_parameters', None) + self.scoring_profile = kwargs.get('scoring_profile', None) + self.search_fields = kwargs.get('search_fields', None) + self.search_mode = kwargs.get('search_mode', None) + self.select = kwargs.get('select', None) + self.skip = kwargs.get('skip', None) + self.top = kwargs.get('top', None) + + +class SearchRequest(msrest.serialization.Model): + """Parameters for filtering, sorting, faceting, paging, and other search query behaviors. + + :param include_total_result_count: A value that specifies whether to fetch the total count of + results. Default is false. Setting this value to true may have a performance impact. Note that + the count returned is an approximation. + :type include_total_result_count: bool + :param facets: The list of facet expressions to apply to the search query. Each facet + expression contains a field name, optionally followed by a comma-separated list of name:value + pairs. + :type facets: list[str] + :param filter: The OData $filter expression to apply to the search query. + :type filter: str + :param highlight_fields: The comma-separated list of field names to use for hit highlights. + Only searchable fields can be used for hit highlighting. + :type highlight_fields: str + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. Default is </em>. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. Default is <em>. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a search query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 100. + :type minimum_coverage: float + :param order_by: The comma-separated list of OData $orderby expressions by which to sort the + results. Each expression can be either a field name or a call to either the geo.distance() or + the search.score() functions. Each expression can be followed by asc to indicate ascending, or + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no $orderby is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: str + :param query_type: A value that specifies the syntax of the search query. The default is + 'simple'. Use 'full' if your query uses the Lucene query syntax. Possible values include: + 'simple', 'full'. + :type query_type: str or ~search_index_client.models.QueryType + :param scoring_parameters: The list of parameter values to be used in scoring functions (for + example, referencePointParameter) using the format name-values. For example, if the scoring + profile defines a function with a parameter called 'mylocation' the parameter string would be + "mylocation--122.2,44.8" (without the quotes). + :type scoring_parameters: list[str] + :param scoring_profile: The name of a scoring profile to evaluate match scores for matching + documents in order to sort the results. + :type scoring_profile: str + :param search_text: A full-text search query expression; Use "*" or omit this parameter to + match all documents. + :type search_text: str + :param search_fields: The comma-separated list of field names to which to scope the full-text + search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the + field names of each fielded search expression take precedence over any field names listed in + this parameter. + :type search_fields: str + :param search_mode: A value that specifies whether any or all of the search terms must be + matched in order to count the document as a match. Possible values include: 'any', 'all'. + :type search_mode: str or ~search_index_client.models.SearchMode + :param select: The comma-separated list of fields to retrieve. If unspecified, all fields + marked as retrievable in the schema are included. + :type select: str + :param skip: The number of search results to skip. This value cannot be greater than 100,000. + If you need to scan documents in sequence, but cannot use skip due to this limitation, consider + using orderby on a totally-ordered key and filter with a range query instead. + :type skip: int + :param top: The number of search results to retrieve. This can be used in conjunction with + $skip to implement client-side paging of search results. If results are truncated due to + server-side paging, the response will include a continuation token that can be used to issue + another Search request for the next page of results. + :type top: int + """ + + _attribute_map = { + 'include_total_result_count': {'key': 'count', 'type': 'bool'}, + 'facets': {'key': 'facets', 'type': '[str]'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'highlight_fields': {'key': 'highlight', 'type': 'str'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'orderby', 'type': 'str'}, + 'query_type': {'key': 'queryType', 'type': 'str'}, + 'scoring_parameters': {'key': 'scoringParameters', 'type': '[str]'}, + 'scoring_profile': {'key': 'scoringProfile', 'type': 'str'}, + 'search_text': {'key': 'search', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'search_mode': {'key': 'searchMode', 'type': 'str'}, + 'select': {'key': 'select', 'type': 'str'}, + 'skip': {'key': 'skip', 'type': 'int'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchRequest, self).__init__(**kwargs) + self.include_total_result_count = kwargs.get('include_total_result_count', None) + self.facets = kwargs.get('facets', None) + self.filter = kwargs.get('filter', None) + self.highlight_fields = kwargs.get('highlight_fields', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.order_by = kwargs.get('order_by', None) + self.query_type = kwargs.get('query_type', None) + self.scoring_parameters = kwargs.get('scoring_parameters', None) + self.scoring_profile = kwargs.get('scoring_profile', None) + self.search_text = kwargs.get('search_text', None) + self.search_fields = kwargs.get('search_fields', None) + self.search_mode = kwargs.get('search_mode', None) + self.select = kwargs.get('select', None) + self.skip = kwargs.get('skip', None) + self.top = kwargs.get('top', None) + + +class SearchResult(msrest.serialization.Model): + """Contains a document found by a search query, plus associated metadata. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar score: Required. The relevance score of the document compared to other documents returned + by the query. + :vartype score: float + :ivar highlights: Text fragments from the document that indicate the matching search terms, + organized by each applicable field; null if hit highlighting was not enabled for the query. + :vartype highlights: dict[str, list[str]] + """ + + _validation = { + 'score': {'required': True, 'readonly': True}, + 'highlights': {'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'score': {'key': '@search\\.score', 'type': 'float'}, + 'highlights': {'key': '@search\\.highlights', 'type': '{[str]}'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchResult, self).__init__(**kwargs) + self.additional_properties = kwargs.get('additional_properties', None) + self.score = None + self.highlights = None + + +class SuggestDocumentsResult(msrest.serialization.Model): + """Response containing suggestion query results from an index. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar results: Required. The sequence of results returned by the query. + :vartype results: list[~search_index_client.models.SuggestResult] + :ivar coverage: A value indicating the percentage of the index that was included in the query, + or null if minimumCoverage was not set in the request. + :vartype coverage: float + """ + + _validation = { + 'results': {'required': True, 'readonly': True}, + 'coverage': {'readonly': True}, + } + + _attribute_map = { + 'results': {'key': 'value', 'type': '[SuggestResult]'}, + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + } + + def __init__( + self, + **kwargs + ): + super(SuggestDocumentsResult, self).__init__(**kwargs) + self.results = None + self.coverage = None + + +class SuggestOptions(msrest.serialization.Model): + """Parameter group. + + :param filter: An OData expression that filters the documents considered for suggestions. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the suggestions + query. Default is false. When set to true, the query will find terms even if there's a + substituted or missing character in the search text. While this provides a better experience in + some scenarios, it comes at a performance cost as fuzzy suggestions queries are slower and + consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a suggestions query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param order_by: The list of OData $orderby expressions by which to sort the results. Each + expression can be either a field name or a call to either the geo.distance() or the + search.score() functions. Each expression can be followed by asc to indicate ascending, or desc + to indicate descending. The default is ascending order. Ties will be broken by the match scores + of documents. If no $orderby is specified, the default sort order is descending by document + match score. There can be at most 32 $orderby clauses. + :type order_by: list[str] + :param search_fields: The list of field names to search for the specified search text. Target + fields must be included in the specified suggester. + :type search_fields: list[str] + :param select: The list of fields to retrieve. If unspecified, only the key field will be + included in the results. + :type select: list[str] + :param top: The number of suggestions to retrieve. The value must be a number between 1 and + 100. The default is 5. + :type top: int + """ + + _attribute_map = { + 'filter': {'key': '$filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'UseFuzzyMatching', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'OrderBy', 'type': '[str]'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'select': {'key': '$select', 'type': '[str]'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(SuggestOptions, self).__init__(**kwargs) + self.filter = kwargs.get('filter', None) + self.use_fuzzy_matching = kwargs.get('use_fuzzy_matching', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.order_by = kwargs.get('order_by', None) + self.search_fields = kwargs.get('search_fields', None) + self.select = kwargs.get('select', None) + self.top = kwargs.get('top', None) + + +class SuggestRequest(msrest.serialization.Model): + """Parameters for filtering, sorting, fuzzy matching, and other suggestions query behaviors. + + All required parameters must be populated in order to send to Azure. + + :param filter: An OData expression that filters the documents considered for suggestions. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the suggestion + query. Default is false. When set to true, the query will find suggestions even if there's a + substituted or missing character in the search text. While this provides a better experience in + some scenarios, it comes at a performance cost as fuzzy suggestion searches are slower and + consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a suggestion query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param order_by: The comma-separated list of OData $orderby expressions by which to sort the + results. Each expression can be either a field name or a call to either the geo.distance() or + the search.score() functions. Each expression can be followed by asc to indicate ascending, or + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no $orderby is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: str + :param search_text: Required. The search text to use to suggest documents. Must be at least 1 + character, and no more than 100 characters. + :type search_text: str + :param search_fields: The comma-separated list of field names to search for the specified + search text. Target fields must be included in the specified suggester. + :type search_fields: str + :param select: The comma-separated list of fields to retrieve. If unspecified, only the key + field will be included in the results. + :type select: str + :param suggester_name: Required. The name of the suggester as specified in the suggesters + collection that's part of the index definition. + :type suggester_name: str + :param top: The number of suggestions to retrieve. This must be a value between 1 and 100. The + default is 5. + :type top: int + """ + + _validation = { + 'search_text': {'required': True}, + 'suggester_name': {'required': True}, + } + + _attribute_map = { + 'filter': {'key': 'filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'fuzzy', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'orderby', 'type': 'str'}, + 'search_text': {'key': 'search', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'select': {'key': 'select', 'type': 'str'}, + 'suggester_name': {'key': 'suggesterName', 'type': 'str'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(SuggestRequest, self).__init__(**kwargs) + self.filter = kwargs.get('filter', None) + self.use_fuzzy_matching = kwargs.get('use_fuzzy_matching', None) + self.highlight_post_tag = kwargs.get('highlight_post_tag', None) + self.highlight_pre_tag = kwargs.get('highlight_pre_tag', None) + self.minimum_coverage = kwargs.get('minimum_coverage', None) + self.order_by = kwargs.get('order_by', None) + self.search_text = kwargs.get('search_text', None) + self.search_fields = kwargs.get('search_fields', None) + self.select = kwargs.get('select', None) + self.suggester_name = kwargs.get('suggester_name', None) + self.top = kwargs.get('top', None) + + +class SuggestResult(msrest.serialization.Model): + """A result containing a document found by a suggestion query, plus associated metadata. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar text: Required. The text of the suggestion result. + :vartype text: str + """ + + _validation = { + 'text': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'text': {'key': '@search\\.text', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SuggestResult, self).__init__(**kwargs) + self.additional_properties = kwargs.get('additional_properties', None) + self.text = None diff --git a/sdk/search/azure-search/azure/search/_index/_generated/models/_models_py3.py b/sdk/search/azure-search/azure/search/_index/_generated/models/_models_py3.py new file mode 100644 index 000000000000..4b8f7bda6f7f --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/models/_models_py3.py @@ -0,0 +1,1085 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Dict, List, Optional, Union + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class AutocompleteItem(msrest.serialization.Model): + """The result of Autocomplete requests. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar text: Required. The completed term. + :vartype text: str + :ivar query_plus_text: Required. The query along with the completed term. + :vartype query_plus_text: str + """ + + _validation = { + 'text': {'required': True, 'readonly': True}, + 'query_plus_text': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'text': {'key': 'text', 'type': 'str'}, + 'query_plus_text': {'key': 'queryPlusText', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteItem, self).__init__(**kwargs) + self.text = None + self.query_plus_text = None + + +class AutocompleteOptions(msrest.serialization.Model): + """Parameter group. + + :param autocomplete_mode: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use + 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing + auto-completed terms. Possible values include: 'oneTerm', 'twoTerms', 'oneTermWithContext'. + :type autocomplete_mode: str or ~search_index_client.models.AutocompleteMode + :param filter: An OData expression that filters the documents used to produce completed terms + for the Autocomplete result. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the + autocomplete query. Default is false. When set to true, the query will find terms even if + there's a substituted or missing character in the search text. While this provides a better + experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are + slower and consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by an autocomplete query in order for the query to be reported as a success. + This parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param search_fields: The list of field names to consider when querying for auto-completed + terms. Target fields must be included in the specified suggester. + :type search_fields: list[str] + :param top: The number of auto-completed terms to retrieve. This must be a value between 1 and + 100. The default is 5. + :type top: int + """ + + _attribute_map = { + 'autocomplete_mode': {'key': 'autocompleteMode', 'type': 'str'}, + 'filter': {'key': '$filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'UseFuzzyMatching', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + *, + autocomplete_mode: Optional[Union[str, "AutocompleteMode"]] = None, + filter: Optional[str] = None, + use_fuzzy_matching: Optional[bool] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + search_fields: Optional[List[str]] = None, + top: Optional[int] = None, + **kwargs + ): + super(AutocompleteOptions, self).__init__(**kwargs) + self.autocomplete_mode = autocomplete_mode + self.filter = filter + self.use_fuzzy_matching = use_fuzzy_matching + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.search_fields = search_fields + self.top = top + + +class AutocompleteRequest(msrest.serialization.Model): + """Parameters for fuzzy matching, and other autocomplete query behaviors. + + All required parameters must be populated in order to send to Azure. + + :param search_text: Required. The search text on which to base autocomplete results. + :type search_text: str + :param autocomplete_mode: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use + 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing + auto-completed terms. Possible values include: 'oneTerm', 'twoTerms', 'oneTermWithContext'. + :type autocomplete_mode: str or ~search_index_client.models.AutocompleteMode + :param filter: An OData expression that filters the documents used to produce completed terms + for the Autocomplete result. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the + autocomplete query. Default is false. When set to true, the query will autocomplete terms even + if there's a substituted or missing character in the search text. While this provides a better + experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are + slower and consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by an autocomplete query in order for the query to be reported as a success. + This parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param search_fields: The comma-separated list of field names to consider when querying for + auto-completed terms. Target fields must be included in the specified suggester. + :type search_fields: str + :param suggester_name: Required. The name of the suggester as specified in the suggesters + collection that's part of the index definition. + :type suggester_name: str + :param top: The number of auto-completed terms to retrieve. This must be a value between 1 and + 100. The default is 5. + :type top: int + """ + + _validation = { + 'search_text': {'required': True}, + 'suggester_name': {'required': True}, + } + + _attribute_map = { + 'search_text': {'key': 'search', 'type': 'str'}, + 'autocomplete_mode': {'key': 'autocompleteMode', 'type': 'str'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'fuzzy', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'suggester_name': {'key': 'suggesterName', 'type': 'str'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + *, + search_text: str, + suggester_name: str, + autocomplete_mode: Optional[Union[str, "AutocompleteMode"]] = None, + filter: Optional[str] = None, + use_fuzzy_matching: Optional[bool] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + search_fields: Optional[str] = None, + top: Optional[int] = None, + **kwargs + ): + super(AutocompleteRequest, self).__init__(**kwargs) + self.search_text = search_text + self.autocomplete_mode = autocomplete_mode + self.filter = filter + self.use_fuzzy_matching = use_fuzzy_matching + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.search_fields = search_fields + self.suggester_name = suggester_name + self.top = top + + +class AutocompleteResult(msrest.serialization.Model): + """The result of Autocomplete query. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar coverage: A value indicating the percentage of the index that was considered by the + autocomplete request, or null if minimumCoverage was not specified in the request. + :vartype coverage: float + :ivar results: Required. The list of returned Autocompleted items. + :vartype results: list[~search_index_client.models.AutocompleteItem] + """ + + _validation = { + 'coverage': {'readonly': True}, + 'results': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + 'results': {'key': 'value', 'type': '[AutocompleteItem]'}, + } + + def __init__( + self, + **kwargs + ): + super(AutocompleteResult, self).__init__(**kwargs) + self.coverage = None + self.results = None + + +class FacetResult(msrest.serialization.Model): + """A single bucket of a facet query result. Reports the number of documents with a field value falling within a particular range or having a particular value or interval. + + Variables are only populated by the server, and will be ignored when sending a request. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar count: The approximate count of documents falling within the bucket described by this + facet. + :vartype count: long + """ + + _validation = { + 'count': {'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'count': {'key': 'count', 'type': 'long'}, + } + + def __init__( + self, + *, + additional_properties: Optional[Dict[str, object]] = None, + **kwargs + ): + super(FacetResult, self).__init__(**kwargs) + self.additional_properties = additional_properties + self.count = None + + +class IndexAction(msrest.serialization.Model): + """Represents an index action that operates on a document. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :param action_type: The operation to perform on a document in an indexing batch. Possible + values include: 'upload', 'merge', 'mergeOrUpload', 'delete'. + :type action_type: str or ~search_index_client.models.IndexActionType + """ + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'action_type': {'key': '@search\\.action', 'type': 'str'}, + } + + def __init__( + self, + *, + additional_properties: Optional[Dict[str, object]] = None, + action_type: Optional[Union[str, "IndexActionType"]] = None, + **kwargs + ): + super(IndexAction, self).__init__(**kwargs) + self.additional_properties = additional_properties + self.action_type = action_type + + +class IndexBatch(msrest.serialization.Model): + """Contains a batch of document write actions to send to the index. + + All required parameters must be populated in order to send to Azure. + + :param actions: Required. The actions in the batch. + :type actions: list[~search_index_client.models.IndexAction] + """ + + _validation = { + 'actions': {'required': True}, + } + + _attribute_map = { + 'actions': {'key': 'value', 'type': '[IndexAction]'}, + } + + def __init__( + self, + *, + actions: List["IndexAction"], + **kwargs + ): + super(IndexBatch, self).__init__(**kwargs) + self.actions = actions + + +class IndexDocumentsResult(msrest.serialization.Model): + """Response containing the status of operations for all documents in the indexing request. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar results: Required. The list of status information for each document in the indexing + request. + :vartype results: list[~search_index_client.models.IndexingResult] + """ + + _validation = { + 'results': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'results': {'key': 'value', 'type': '[IndexingResult]'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexDocumentsResult, self).__init__(**kwargs) + self.results = None + + +class IndexingResult(msrest.serialization.Model): + """Status of an indexing operation for a single document. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar key: Required. The key of a document that was in the indexing request. + :vartype key: str + :ivar error_message: The error message explaining why the indexing operation failed for the + document identified by the key; null if indexing succeeded. + :vartype error_message: str + :ivar succeeded: Required. A value indicating whether the indexing operation succeeded for the + document identified by the key. + :vartype succeeded: bool + :ivar status_code: Required. The status code of the indexing operation. Possible values + include: 200 for a successful update or delete, 201 for successful document creation, 400 for a + malformed input document, 404 for document not found, 409 for a version conflict, 422 when the + index is temporarily unavailable, or 503 for when the service is too busy. + :vartype status_code: int + """ + + _validation = { + 'key': {'required': True, 'readonly': True}, + 'error_message': {'readonly': True}, + 'succeeded': {'required': True, 'readonly': True}, + 'status_code': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'key': {'key': 'key', 'type': 'str'}, + 'error_message': {'key': 'errorMessage', 'type': 'str'}, + 'succeeded': {'key': 'status', 'type': 'bool'}, + 'status_code': {'key': 'statusCode', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(IndexingResult, self).__init__(**kwargs) + self.key = None + self.error_message = None + self.succeeded = None + self.status_code = None + + +class RequestOptions(msrest.serialization.Model): + """Parameter group. + + :param x_ms_client_request_id: The tracking ID sent with the request to help with debugging. + :type x_ms_client_request_id: str + """ + + _attribute_map = { + 'x_ms_client_request_id': {'key': 'x-ms-client-request-id', 'type': 'str'}, + } + + def __init__( + self, + *, + x_ms_client_request_id: Optional[str] = None, + **kwargs + ): + super(RequestOptions, self).__init__(**kwargs) + self.x_ms_client_request_id = x_ms_client_request_id + + +class SearchDocumentsResult(msrest.serialization.Model): + """Response containing search results from an index. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar count: The total count of results found by the search operation, or null if the count was + not requested. If present, the count may be greater than the number of results in this + response. This can happen if you use the $top or $skip parameters, or if Azure Cognitive Search + can't return all the requested documents in a single Search response. + :vartype count: long + :ivar coverage: A value indicating the percentage of the index that was included in the query, + or null if minimumCoverage was not specified in the request. + :vartype coverage: float + :ivar facets: The facet query results for the search operation, organized as a collection of + buckets for each faceted field; null if the query did not include any facet expressions. + :vartype facets: dict[str, list[~search_index_client.models.FacetResult]] + :ivar next_page_parameters: Continuation JSON payload returned when Azure Cognitive Search + can't return all the requested results in a single Search response. You can use this JSON along + with @odata.nextLink to formulate another POST Search request to get the next part of the + search response. + :vartype next_page_parameters: ~search_index_client.models.SearchRequest + :ivar results: Required. The sequence of results returned by the query. + :vartype results: list[~search_index_client.models.SearchResult] + :ivar next_link: Continuation URL returned when Azure Cognitive Search can't return all the + requested results in a single Search response. You can use this URL to formulate another GET or + POST Search request to get the next part of the search response. Make sure to use the same verb + (GET or POST) as the request that produced this response. + :vartype next_link: str + """ + + _validation = { + 'count': {'readonly': True}, + 'coverage': {'readonly': True}, + 'facets': {'readonly': True}, + 'next_page_parameters': {'readonly': True}, + 'results': {'required': True, 'readonly': True}, + 'next_link': {'readonly': True}, + } + + _attribute_map = { + 'count': {'key': '@odata\\.count', 'type': 'long'}, + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + 'facets': {'key': '@search\\.facets', 'type': '{[FacetResult]}'}, + 'next_page_parameters': {'key': '@search\\.nextPageParameters', 'type': 'SearchRequest'}, + 'results': {'key': 'value', 'type': '[SearchResult]'}, + 'next_link': {'key': '@odata\\.nextLink', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchDocumentsResult, self).__init__(**kwargs) + self.count = None + self.coverage = None + self.facets = None + self.next_page_parameters = None + self.results = None + self.next_link = None + + +class SearchErrorException(HttpResponseError): + """Server responded with exception of type: 'SearchError'. + + :param response: Server response to be deserialized. + :param error_model: A deserialized model of the response body as model. + """ + + def __init__(self, response, error_model): + self.error = error_model + super(SearchErrorException, self).__init__(response=response, error_model=error_model) + + @classmethod + def from_response(cls, response, deserialize): + """Deserialize this response as this exception, or a subclass of this exception. + + :param response: Server response to be deserialized. + :param deserialize: A deserializer + """ + model_name = 'SearchError' + error = deserialize(model_name, response) + if error is None: + error = deserialize.dependencies[model_name]() + return error._EXCEPTION_TYPE(response, error) + + +class SearchError(msrest.serialization.Model): + """Describes an error condition for the Azure Cognitive Search API. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar code: One of a server-defined set of error codes. + :vartype code: str + :ivar message: Required. A human-readable representation of the error. + :vartype message: str + :ivar details: An array of details about specific errors that led to this reported error. + :vartype details: list[~search_index_client.models.SearchError] + """ + _EXCEPTION_TYPE = SearchErrorException + + _validation = { + 'code': {'readonly': True}, + 'message': {'required': True, 'readonly': True}, + 'details': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[SearchError]'}, + } + + def __init__( + self, + **kwargs + ): + super(SearchError, self).__init__(**kwargs) + self.code = None + self.message = None + self.details = None + + +class SearchOptions(msrest.serialization.Model): + """Parameter group. + + :param include_total_result_count: A value that specifies whether to fetch the total count of + results. Default is false. Setting this value to true may have a performance impact. Note that + the count returned is an approximation. + :type include_total_result_count: bool + :param facets: The list of facet expressions to apply to the search query. Each facet + expression contains a field name, optionally followed by a comma-separated list of name:value + pairs. + :type facets: list[str] + :param filter: The OData $filter expression to apply to the search query. + :type filter: str + :param highlight_fields: The list of field names to use for hit highlights. Only searchable + fields can be used for hit highlighting. + :type highlight_fields: list[str] + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. Default is </em>. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. Default is <em>. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a search query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 100. + :type minimum_coverage: float + :param order_by: The list of OData $orderby expressions by which to sort the results. Each + expression can be either a field name or a call to either the geo.distance() or the + search.score() functions. Each expression can be followed by asc to indicate ascending, and + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no OrderBy is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: list[str] + :param query_type: A value that specifies the syntax of the search query. The default is + 'simple'. Use 'full' if your query uses the Lucene query syntax. Possible values include: + 'simple', 'full'. + :type query_type: str or ~search_index_client.models.QueryType + :param scoring_parameters: The list of parameter values to be used in scoring functions (for + example, referencePointParameter) using the format name-values. For example, if the scoring + profile defines a function with a parameter called 'mylocation' the parameter string would be + "mylocation--122.2,44.8" (without the quotes). + :type scoring_parameters: list[str] + :param scoring_profile: The name of a scoring profile to evaluate match scores for matching + documents in order to sort the results. + :type scoring_profile: str + :param search_fields: The list of field names to which to scope the full-text search. When + using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of + each fielded search expression take precedence over any field names listed in this parameter. + :type search_fields: list[str] + :param search_mode: A value that specifies whether any or all of the search terms must be + matched in order to count the document as a match. Possible values include: 'any', 'all'. + :type search_mode: str or ~search_index_client.models.SearchMode + :param select: The list of fields to retrieve. If unspecified, all fields marked as retrievable + in the schema are included. + :type select: list[str] + :param skip: The number of search results to skip. This value cannot be greater than 100,000. + If you need to scan documents in sequence, but cannot use $skip due to this limitation, + consider using $orderby on a totally-ordered key and $filter with a range query instead. + :type skip: int + :param top: The number of search results to retrieve. This can be used in conjunction with + $skip to implement client-side paging of search results. If results are truncated due to + server-side paging, the response will include a continuation token that can be used to issue + another Search request for the next page of results. + :type top: int + """ + + _attribute_map = { + 'include_total_result_count': {'key': 'IncludeTotalResultCount', 'type': 'bool'}, + 'facets': {'key': 'Facets', 'type': '[str]'}, + 'filter': {'key': '$filter', 'type': 'str'}, + 'highlight_fields': {'key': 'HighlightFields', 'type': '[str]'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'OrderBy', 'type': '[str]'}, + 'query_type': {'key': 'queryType', 'type': 'str'}, + 'scoring_parameters': {'key': 'ScoringParameters', 'type': '[str]'}, + 'scoring_profile': {'key': 'scoringProfile', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'search_mode': {'key': 'searchMode', 'type': 'str'}, + 'select': {'key': '$select', 'type': '[str]'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + *, + include_total_result_count: Optional[bool] = None, + facets: Optional[List[str]] = None, + filter: Optional[str] = None, + highlight_fields: Optional[List[str]] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + order_by: Optional[List[str]] = None, + query_type: Optional[Union[str, "QueryType"]] = None, + scoring_parameters: Optional[List[str]] = None, + scoring_profile: Optional[str] = None, + search_fields: Optional[List[str]] = None, + search_mode: Optional[Union[str, "SearchMode"]] = None, + select: Optional[List[str]] = None, + skip: Optional[int] = None, + top: Optional[int] = None, + **kwargs + ): + super(SearchOptions, self).__init__(**kwargs) + self.include_total_result_count = include_total_result_count + self.facets = facets + self.filter = filter + self.highlight_fields = highlight_fields + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.order_by = order_by + self.query_type = query_type + self.scoring_parameters = scoring_parameters + self.scoring_profile = scoring_profile + self.search_fields = search_fields + self.search_mode = search_mode + self.select = select + self.skip = skip + self.top = top + + +class SearchRequest(msrest.serialization.Model): + """Parameters for filtering, sorting, faceting, paging, and other search query behaviors. + + :param include_total_result_count: A value that specifies whether to fetch the total count of + results. Default is false. Setting this value to true may have a performance impact. Note that + the count returned is an approximation. + :type include_total_result_count: bool + :param facets: The list of facet expressions to apply to the search query. Each facet + expression contains a field name, optionally followed by a comma-separated list of name:value + pairs. + :type facets: list[str] + :param filter: The OData $filter expression to apply to the search query. + :type filter: str + :param highlight_fields: The comma-separated list of field names to use for hit highlights. + Only searchable fields can be used for hit highlighting. + :type highlight_fields: str + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. Default is </em>. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. Default is <em>. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a search query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 100. + :type minimum_coverage: float + :param order_by: The comma-separated list of OData $orderby expressions by which to sort the + results. Each expression can be either a field name or a call to either the geo.distance() or + the search.score() functions. Each expression can be followed by asc to indicate ascending, or + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no $orderby is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: str + :param query_type: A value that specifies the syntax of the search query. The default is + 'simple'. Use 'full' if your query uses the Lucene query syntax. Possible values include: + 'simple', 'full'. + :type query_type: str or ~search_index_client.models.QueryType + :param scoring_parameters: The list of parameter values to be used in scoring functions (for + example, referencePointParameter) using the format name-values. For example, if the scoring + profile defines a function with a parameter called 'mylocation' the parameter string would be + "mylocation--122.2,44.8" (without the quotes). + :type scoring_parameters: list[str] + :param scoring_profile: The name of a scoring profile to evaluate match scores for matching + documents in order to sort the results. + :type scoring_profile: str + :param search_text: A full-text search query expression; Use "*" or omit this parameter to + match all documents. + :type search_text: str + :param search_fields: The comma-separated list of field names to which to scope the full-text + search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the + field names of each fielded search expression take precedence over any field names listed in + this parameter. + :type search_fields: str + :param search_mode: A value that specifies whether any or all of the search terms must be + matched in order to count the document as a match. Possible values include: 'any', 'all'. + :type search_mode: str or ~search_index_client.models.SearchMode + :param select: The comma-separated list of fields to retrieve. If unspecified, all fields + marked as retrievable in the schema are included. + :type select: str + :param skip: The number of search results to skip. This value cannot be greater than 100,000. + If you need to scan documents in sequence, but cannot use skip due to this limitation, consider + using orderby on a totally-ordered key and filter with a range query instead. + :type skip: int + :param top: The number of search results to retrieve. This can be used in conjunction with + $skip to implement client-side paging of search results. If results are truncated due to + server-side paging, the response will include a continuation token that can be used to issue + another Search request for the next page of results. + :type top: int + """ + + _attribute_map = { + 'include_total_result_count': {'key': 'count', 'type': 'bool'}, + 'facets': {'key': 'facets', 'type': '[str]'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'highlight_fields': {'key': 'highlight', 'type': 'str'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'orderby', 'type': 'str'}, + 'query_type': {'key': 'queryType', 'type': 'str'}, + 'scoring_parameters': {'key': 'scoringParameters', 'type': '[str]'}, + 'scoring_profile': {'key': 'scoringProfile', 'type': 'str'}, + 'search_text': {'key': 'search', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'search_mode': {'key': 'searchMode', 'type': 'str'}, + 'select': {'key': 'select', 'type': 'str'}, + 'skip': {'key': 'skip', 'type': 'int'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + *, + include_total_result_count: Optional[bool] = None, + facets: Optional[List[str]] = None, + filter: Optional[str] = None, + highlight_fields: Optional[str] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + order_by: Optional[str] = None, + query_type: Optional[Union[str, "QueryType"]] = None, + scoring_parameters: Optional[List[str]] = None, + scoring_profile: Optional[str] = None, + search_text: Optional[str] = None, + search_fields: Optional[str] = None, + search_mode: Optional[Union[str, "SearchMode"]] = None, + select: Optional[str] = None, + skip: Optional[int] = None, + top: Optional[int] = None, + **kwargs + ): + super(SearchRequest, self).__init__(**kwargs) + self.include_total_result_count = include_total_result_count + self.facets = facets + self.filter = filter + self.highlight_fields = highlight_fields + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.order_by = order_by + self.query_type = query_type + self.scoring_parameters = scoring_parameters + self.scoring_profile = scoring_profile + self.search_text = search_text + self.search_fields = search_fields + self.search_mode = search_mode + self.select = select + self.skip = skip + self.top = top + + +class SearchResult(msrest.serialization.Model): + """Contains a document found by a search query, plus associated metadata. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar score: Required. The relevance score of the document compared to other documents returned + by the query. + :vartype score: float + :ivar highlights: Text fragments from the document that indicate the matching search terms, + organized by each applicable field; null if hit highlighting was not enabled for the query. + :vartype highlights: dict[str, list[str]] + """ + + _validation = { + 'score': {'required': True, 'readonly': True}, + 'highlights': {'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'score': {'key': '@search\\.score', 'type': 'float'}, + 'highlights': {'key': '@search\\.highlights', 'type': '{[str]}'}, + } + + def __init__( + self, + *, + additional_properties: Optional[Dict[str, object]] = None, + **kwargs + ): + super(SearchResult, self).__init__(**kwargs) + self.additional_properties = additional_properties + self.score = None + self.highlights = None + + +class SuggestDocumentsResult(msrest.serialization.Model): + """Response containing suggestion query results from an index. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar results: Required. The sequence of results returned by the query. + :vartype results: list[~search_index_client.models.SuggestResult] + :ivar coverage: A value indicating the percentage of the index that was included in the query, + or null if minimumCoverage was not set in the request. + :vartype coverage: float + """ + + _validation = { + 'results': {'required': True, 'readonly': True}, + 'coverage': {'readonly': True}, + } + + _attribute_map = { + 'results': {'key': 'value', 'type': '[SuggestResult]'}, + 'coverage': {'key': '@search\\.coverage', 'type': 'float'}, + } + + def __init__( + self, + **kwargs + ): + super(SuggestDocumentsResult, self).__init__(**kwargs) + self.results = None + self.coverage = None + + +class SuggestOptions(msrest.serialization.Model): + """Parameter group. + + :param filter: An OData expression that filters the documents considered for suggestions. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the suggestions + query. Default is false. When set to true, the query will find terms even if there's a + substituted or missing character in the search text. While this provides a better experience in + some scenarios, it comes at a performance cost as fuzzy suggestions queries are slower and + consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a suggestions query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param order_by: The list of OData $orderby expressions by which to sort the results. Each + expression can be either a field name or a call to either the geo.distance() or the + search.score() functions. Each expression can be followed by asc to indicate ascending, or desc + to indicate descending. The default is ascending order. Ties will be broken by the match scores + of documents. If no $orderby is specified, the default sort order is descending by document + match score. There can be at most 32 $orderby clauses. + :type order_by: list[str] + :param search_fields: The list of field names to search for the specified search text. Target + fields must be included in the specified suggester. + :type search_fields: list[str] + :param select: The list of fields to retrieve. If unspecified, only the key field will be + included in the results. + :type select: list[str] + :param top: The number of suggestions to retrieve. The value must be a number between 1 and + 100. The default is 5. + :type top: int + """ + + _attribute_map = { + 'filter': {'key': '$filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'UseFuzzyMatching', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'OrderBy', 'type': '[str]'}, + 'search_fields': {'key': 'searchFields', 'type': '[str]'}, + 'select': {'key': '$select', 'type': '[str]'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + *, + filter: Optional[str] = None, + use_fuzzy_matching: Optional[bool] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + order_by: Optional[List[str]] = None, + search_fields: Optional[List[str]] = None, + select: Optional[List[str]] = None, + top: Optional[int] = None, + **kwargs + ): + super(SuggestOptions, self).__init__(**kwargs) + self.filter = filter + self.use_fuzzy_matching = use_fuzzy_matching + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.order_by = order_by + self.search_fields = search_fields + self.select = select + self.top = top + + +class SuggestRequest(msrest.serialization.Model): + """Parameters for filtering, sorting, fuzzy matching, and other suggestions query behaviors. + + All required parameters must be populated in order to send to Azure. + + :param filter: An OData expression that filters the documents considered for suggestions. + :type filter: str + :param use_fuzzy_matching: A value indicating whether to use fuzzy matching for the suggestion + query. Default is false. When set to true, the query will find suggestions even if there's a + substituted or missing character in the search text. While this provides a better experience in + some scenarios, it comes at a performance cost as fuzzy suggestion searches are slower and + consume more resources. + :type use_fuzzy_matching: bool + :param highlight_post_tag: A string tag that is appended to hit highlights. Must be set with + highlightPreTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_post_tag: str + :param highlight_pre_tag: A string tag that is prepended to hit highlights. Must be set with + highlightPostTag. If omitted, hit highlighting of suggestions is disabled. + :type highlight_pre_tag: str + :param minimum_coverage: A number between 0 and 100 indicating the percentage of the index that + must be covered by a suggestion query in order for the query to be reported as a success. This + parameter can be useful for ensuring search availability even for services with only one + replica. The default is 80. + :type minimum_coverage: float + :param order_by: The comma-separated list of OData $orderby expressions by which to sort the + results. Each expression can be either a field name or a call to either the geo.distance() or + the search.score() functions. Each expression can be followed by asc to indicate ascending, or + desc to indicate descending. The default is ascending order. Ties will be broken by the match + scores of documents. If no $orderby is specified, the default sort order is descending by + document match score. There can be at most 32 $orderby clauses. + :type order_by: str + :param search_text: Required. The search text to use to suggest documents. Must be at least 1 + character, and no more than 100 characters. + :type search_text: str + :param search_fields: The comma-separated list of field names to search for the specified + search text. Target fields must be included in the specified suggester. + :type search_fields: str + :param select: The comma-separated list of fields to retrieve. If unspecified, only the key + field will be included in the results. + :type select: str + :param suggester_name: Required. The name of the suggester as specified in the suggesters + collection that's part of the index definition. + :type suggester_name: str + :param top: The number of suggestions to retrieve. This must be a value between 1 and 100. The + default is 5. + :type top: int + """ + + _validation = { + 'search_text': {'required': True}, + 'suggester_name': {'required': True}, + } + + _attribute_map = { + 'filter': {'key': 'filter', 'type': 'str'}, + 'use_fuzzy_matching': {'key': 'fuzzy', 'type': 'bool'}, + 'highlight_post_tag': {'key': 'highlightPostTag', 'type': 'str'}, + 'highlight_pre_tag': {'key': 'highlightPreTag', 'type': 'str'}, + 'minimum_coverage': {'key': 'minimumCoverage', 'type': 'float'}, + 'order_by': {'key': 'orderby', 'type': 'str'}, + 'search_text': {'key': 'search', 'type': 'str'}, + 'search_fields': {'key': 'searchFields', 'type': 'str'}, + 'select': {'key': 'select', 'type': 'str'}, + 'suggester_name': {'key': 'suggesterName', 'type': 'str'}, + 'top': {'key': 'top', 'type': 'int'}, + } + + def __init__( + self, + *, + search_text: str, + suggester_name: str, + filter: Optional[str] = None, + use_fuzzy_matching: Optional[bool] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + minimum_coverage: Optional[float] = None, + order_by: Optional[str] = None, + search_fields: Optional[str] = None, + select: Optional[str] = None, + top: Optional[int] = None, + **kwargs + ): + super(SuggestRequest, self).__init__(**kwargs) + self.filter = filter + self.use_fuzzy_matching = use_fuzzy_matching + self.highlight_post_tag = highlight_post_tag + self.highlight_pre_tag = highlight_pre_tag + self.minimum_coverage = minimum_coverage + self.order_by = order_by + self.search_text = search_text + self.search_fields = search_fields + self.select = select + self.suggester_name = suggester_name + self.top = top + + +class SuggestResult(msrest.serialization.Model): + """A result containing a document found by a suggestion query, plus associated metadata. + + Variables are only populated by the server, and will be ignored when sending a request. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :ivar text: Required. The text of the suggestion result. + :vartype text: str + """ + + _validation = { + 'text': {'required': True, 'readonly': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'text': {'key': '@search\\.text', 'type': 'str'}, + } + + def __init__( + self, + *, + additional_properties: Optional[Dict[str, object]] = None, + **kwargs + ): + super(SuggestResult, self).__init__(**kwargs) + self.additional_properties = additional_properties + self.text = None diff --git a/sdk/search/azure-search/azure/search/_index/_generated/models/_search_index_client_enums.py b/sdk/search/azure-search/azure/search/_index/_generated/models/_search_index_client_enums.py new file mode 100644 index 000000000000..ec3e46a89f5a --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/models/_search_index_client_enums.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum + +class IndexActionType(str, Enum): + """The operation to perform on a document in an indexing batch. + """ + + upload = "upload" + merge = "merge" + merge_or_upload = "mergeOrUpload" + delete = "delete" + +class QueryType(str, Enum): + + simple = "simple" + full = "full" + +class SearchMode(str, Enum): + + any = "any" + all = "all" + +class AutocompleteMode(str, Enum): + + one_term = "oneTerm" + two_terms = "twoTerms" + one_term_with_context = "oneTermWithContext" diff --git a/sdk/search/azure-search/azure/search/_index/_generated/operations/__init__.py b/sdk/search/azure-search/azure/search/_index/_generated/operations/__init__.py new file mode 100644 index 000000000000..1de4dc8bc765 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/operations/__init__.py @@ -0,0 +1,11 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._documents_operations import DocumentsOperations + +__all__ = [ + 'DocumentsOperations', +] diff --git a/sdk/search/azure-search/azure/search/_index/_generated/operations/_documents_operations.py b/sdk/search/azure-search/azure/search/_index/_generated/operations/_documents_operations.py new file mode 100644 index 000000000000..718835b3c195 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/operations/_documents_operations.py @@ -0,0 +1,776 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.0.6246, generator: {generator}) +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union +import warnings + +from azure.core.exceptions import map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse + +from .. import models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class DocumentsOperations(object): + """DocumentsOperations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~search_index_client.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def count( + self, + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> int + """Queries the number of documents in the index. + + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: long or the result of cls(response) + :rtype: long + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[int] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.count.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('long', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + count.metadata = {'url': '/docs/$count'} + + def search_get( + self, + search_text=None, # type: Optional[str] + search_options=None, # type: Optional["models.SearchOptions"] + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.SearchDocumentsResult" + """Searches for documents in the index. + + :param search_text: A full-text search query expression; Use "*" or omit this parameter to + match all documents. + :type search_text: str + :param search_options: Parameter group. + :type search_options: ~search_index_client.models.SearchOptions + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SearchDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SearchDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SearchDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _include_total_result_count = None + _facets = None + _filter = None + _highlight_fields = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _order_by = None + _query_type = None + _scoring_parameters = None + _scoring_profile = None + _search_fields = None + _search_mode = None + _select = None + _skip = None + _top = None + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + if search_options is not None: + _include_total_result_count = search_options.include_total_result_count + _facets = search_options.facets + _filter = search_options.filter + _highlight_fields = search_options.highlight_fields + _highlight_post_tag = search_options.highlight_post_tag + _highlight_pre_tag = search_options.highlight_pre_tag + _minimum_coverage = search_options.minimum_coverage + _order_by = search_options.order_by + _query_type = search_options.query_type + _scoring_parameters = search_options.scoring_parameters + _scoring_profile = search_options.scoring_profile + _search_fields = search_options.search_fields + _search_mode = search_options.search_mode + _select = search_options.select + _skip = search_options.skip + _top = search_options.top + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.search_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if search_text is not None: + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + if _include_total_result_count is not None: + query_parameters['$count'] = self._serialize.query("include_total_result_count", _include_total_result_count, 'bool') + if _facets is not None: + query_parameters['facet'] = self._serialize.query("facets", _facets, '[str]', div=',') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _highlight_fields is not None: + query_parameters['highlight'] = self._serialize.query("highlight_fields", _highlight_fields, '[str]') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _order_by is not None: + query_parameters['$orderby'] = self._serialize.query("order_by", _order_by, '[str]') + if _query_type is not None: + query_parameters['queryType'] = self._serialize.query("query_type", _query_type, 'str') + if _scoring_parameters is not None: + query_parameters['scoringParameter'] = self._serialize.query("scoring_parameters", _scoring_parameters, '[str]', div=',') + if _scoring_profile is not None: + query_parameters['scoringProfile'] = self._serialize.query("scoring_profile", _scoring_profile, 'str') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _search_mode is not None: + query_parameters['searchMode'] = self._serialize.query("search_mode", _search_mode, 'str') + if _select is not None: + query_parameters['$select'] = self._serialize.query("select", _select, '[str]') + if _skip is not None: + query_parameters['$skip'] = self._serialize.query("skip", _skip, 'int') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SearchDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + search_get.metadata = {'url': '/docs'} + + def search_post( + self, + search_request, # type: "models.SearchRequest" + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.SearchDocumentsResult" + """Searches for documents in the index. + + :param search_request: The definition of the Search request. + :type search_request: ~search_index_client.models.SearchRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SearchDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SearchDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SearchDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.search_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(search_request, 'SearchRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SearchDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + search_post.metadata = {'url': '/docs/search.post.search'} + + def get( + self, + key, # type: str + selected_fields=None, # type: Optional[List[str]] + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> object + """Retrieves a document from the index. + + :param key: The key of the document to retrieve. + :type key: str + :param selected_fields: List of field names to retrieve for the document; Any field not + retrieved will be missing from the returned document. + :type selected_fields: list[str] + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: object or the result of cls(response) + :rtype: object + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[object] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + 'key': self._serialize.url("key", key, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + if selected_fields is not None: + query_parameters['$select'] = self._serialize.query("selected_fields", selected_fields, '[str]') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('object', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get.metadata = {'url': '/docs(\'{key}\')'} + + def suggest_get( + self, + search_text, # type: str + suggester_name, # type: str + suggest_options=None, # type: Optional["models.SuggestOptions"] + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.SuggestDocumentsResult" + """Suggests documents in the index that match the given partial query text. + + :param search_text: The search text to use to suggest documents. Must be at least 1 character, + and no more than 100 characters. + :type search_text: str + :param suggester_name: The name of the suggester as specified in the suggesters collection + that's part of the index definition. + :type suggester_name: str + :param suggest_options: Parameter group. + :type suggest_options: ~search_index_client.models.SuggestOptions + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SuggestDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SuggestDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SuggestDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _filter = None + _use_fuzzy_matching = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _order_by = None + _search_fields = None + _select = None + _top = None + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + if suggest_options is not None: + _filter = suggest_options.filter + _use_fuzzy_matching = suggest_options.use_fuzzy_matching + _highlight_post_tag = suggest_options.highlight_post_tag + _highlight_pre_tag = suggest_options.highlight_pre_tag + _minimum_coverage = suggest_options.minimum_coverage + _order_by = suggest_options.order_by + _search_fields = suggest_options.search_fields + _select = suggest_options.select + _top = suggest_options.top + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.suggest_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + query_parameters['suggesterName'] = self._serialize.query("suggester_name", suggester_name, 'str') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _use_fuzzy_matching is not None: + query_parameters['fuzzy'] = self._serialize.query("use_fuzzy_matching", _use_fuzzy_matching, 'bool') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _order_by is not None: + query_parameters['$orderby'] = self._serialize.query("order_by", _order_by, '[str]') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _select is not None: + query_parameters['$select'] = self._serialize.query("select", _select, '[str]') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SuggestDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + suggest_get.metadata = {'url': '/docs/search.suggest'} + + def suggest_post( + self, + suggest_request, # type: "models.SuggestRequest" + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.SuggestDocumentsResult" + """Suggests documents in the index that match the given partial query text. + + :param suggest_request: The Suggest request. + :type suggest_request: ~search_index_client.models.SuggestRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: SuggestDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.SuggestDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.SuggestDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.suggest_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(suggest_request, 'SuggestRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('SuggestDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + suggest_post.metadata = {'url': '/docs/search.post.suggest'} + + def index( + self, + batch, # type: "models.IndexBatch" + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.IndexDocumentsResult" + """Sends a batch of document write actions to the index. + + :param batch: The batch of index actions. + :type batch: ~search_index_client.models.IndexBatch + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: IndexDocumentsResult or the result of cls(response) + :rtype: ~search_index_client.models.IndexDocumentsResult or ~search_index_client.models.IndexDocumentsResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.IndexDocumentsResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.index.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(batch, 'IndexBatch') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 207]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = None + if response.status_code == 200: + deserialized = self._deserialize('IndexDocumentsResult', pipeline_response) + + if response.status_code == 207: + deserialized = self._deserialize('IndexDocumentsResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + index.metadata = {'url': '/docs/search.index'} + + def autocomplete_get( + self, + search_text, # type: str + suggester_name, # type: str + request_options=None, # type: Optional["models.RequestOptions"] + autocomplete_options=None, # type: Optional["models.AutocompleteOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.AutocompleteResult" + """Autocompletes incomplete query terms based on input text and matching terms in the index. + + :param search_text: The incomplete term which should be auto-completed. + :type search_text: str + :param suggester_name: The name of the suggester as specified in the suggesters collection + that's part of the index definition. + :type suggester_name: str + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :param autocomplete_options: Parameter group. + :type autocomplete_options: ~search_index_client.models.AutocompleteOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: AutocompleteResult or the result of cls(response) + :rtype: ~search_index_client.models.AutocompleteResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.AutocompleteResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + _autocomplete_mode = None + _filter = None + _use_fuzzy_matching = None + _highlight_post_tag = None + _highlight_pre_tag = None + _minimum_coverage = None + _search_fields = None + _top = None + if autocomplete_options is not None: + _autocomplete_mode = autocomplete_options.autocomplete_mode + _filter = autocomplete_options.filter + _use_fuzzy_matching = autocomplete_options.use_fuzzy_matching + _highlight_post_tag = autocomplete_options.highlight_post_tag + _highlight_pre_tag = autocomplete_options.highlight_pre_tag + _minimum_coverage = autocomplete_options.minimum_coverage + _search_fields = autocomplete_options.search_fields + _top = autocomplete_options.top + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.autocomplete_get.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + query_parameters['search'] = self._serialize.query("search_text", search_text, 'str') + query_parameters['suggesterName'] = self._serialize.query("suggester_name", suggester_name, 'str') + if _autocomplete_mode is not None: + query_parameters['autocompleteMode'] = self._serialize.query("autocomplete_mode", _autocomplete_mode, 'str') + if _filter is not None: + query_parameters['$filter'] = self._serialize.query("filter", _filter, 'str') + if _use_fuzzy_matching is not None: + query_parameters['fuzzy'] = self._serialize.query("use_fuzzy_matching", _use_fuzzy_matching, 'bool') + if _highlight_post_tag is not None: + query_parameters['highlightPostTag'] = self._serialize.query("highlight_post_tag", _highlight_post_tag, 'str') + if _highlight_pre_tag is not None: + query_parameters['highlightPreTag'] = self._serialize.query("highlight_pre_tag", _highlight_pre_tag, 'str') + if _minimum_coverage is not None: + query_parameters['minimumCoverage'] = self._serialize.query("minimum_coverage", _minimum_coverage, 'float') + if _search_fields is not None: + query_parameters['searchFields'] = self._serialize.query("search_fields", _search_fields, '[str]') + if _top is not None: + query_parameters['$top'] = self._serialize.query("top", _top, 'int') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('AutocompleteResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + autocomplete_get.metadata = {'url': '/docs/search.autocomplete'} + + def autocomplete_post( + self, + autocomplete_request, # type: "models.AutocompleteRequest" + request_options=None, # type: Optional["models.RequestOptions"] + **kwargs # type: Any + ): + # type: (...) -> "models.AutocompleteResult" + """Autocompletes incomplete query terms based on input text and matching terms in the index. + + :param autocomplete_request: The definition of the Autocomplete request. + :type autocomplete_request: ~search_index_client.models.AutocompleteRequest + :param request_options: Parameter group. + :type request_options: ~search_index_client.models.RequestOptions + :keyword callable cls: A custom type or function that will be passed the direct response + :return: AutocompleteResult or the result of cls(response) + :rtype: ~search_index_client.models.AutocompleteResult + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["models.AutocompleteResult"] + error_map = kwargs.pop('error_map', {}) + + _x_ms_client_request_id = None + if request_options is not None: + _x_ms_client_request_id = request_options.x_ms_client_request_id + api_version = "2019-05-06-Preview" + + # Construct URL + url = self.autocomplete_post.metadata['url'] + path_format_arguments = { + 'endpoint': self._serialize.url("self._config.endpoint", self._config.endpoint, 'str', skip_quote=True), + 'indexName': self._serialize.url("self._config.index_name", self._config.index_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + if _x_ms_client_request_id is not None: + header_parameters['x-ms-client-request-id'] = self._serialize.header("x_ms_client_request_id", _x_ms_client_request_id, 'str') + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = kwargs.pop('content_type', 'application/json') + + # Construct and send request + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(autocomplete_request, 'AutocompleteRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise models.SearchErrorException.from_response(response, self._deserialize) + + deserialized = self._deserialize('AutocompleteResult', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + autocomplete_post.metadata = {'url': '/docs/search.post.autocomplete'} diff --git a/sdk/search/azure-search/azure/search/_index/_generated/py.typed b/sdk/search/azure-search/azure/search/_index/_generated/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_generated/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/search/azure-search/azure/search/_index/_index_documents_batch.py b/sdk/search/azure-search/azure/search/_index/_index_documents_batch.py new file mode 100644 index 000000000000..fd02a58e4b28 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_index_documents_batch.py @@ -0,0 +1,119 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +from ._generated.models import IndexAction + +if TYPE_CHECKING: + # pylint:disable=unused-import + from typing import List + + +def flatten_args(args): + # type (Union[List[dict], List[List[dict]]]) -> List[dict] + if len(args) == 1 and isinstance(args[0], (list, tuple)): + return args[0] + return args + + +class IndexDocumentsBatch(object): + """Represent a batch of upate operations for documents in an Azure + Search index. + + Index operations are performed in the order in which they are added + to the batch. + + """ + + def __init__(self): + # type: () -> None + self._actions = [] # type: List[IndexAction] + + def __repr__(self): + # type: () -> str + return "".format(len(self.actions))[:1024] + + def add_upload_documents(self, *documents): + # type (Union[List[dict], List[List[dict]]]) -> None + """Add documents to upload to the Azure search index. + + An upload action is similar to an "upsert" where the document will be + inserted if it is new and updated/replaced if it exists. All fields are + replaced in the update case. + + :param documents: Documents to upload to an Azure search index. May be + a single list of documents, or documents as individual parameters. + :type documents: dict or list[dict] + """ + self._extend_batch(flatten_args(documents), "upload") + + def add_delete_documents(self, *documents): + # type (Union[List[dict], List[List[dict]]]) -> None + """Add documents to delete to the Azure search index. + + Delete removes the specified document from the index. Any field you + specify in a delete operation, other than the key field, will be + ignored. If you want to remove an individual field from a document, use + `merge_documents` instead and set the field explicitly to None. + + Delete operations are idempotent. That is, even if a document key does + not exist in the index, attempting a delete operation with that key will + result in a 200 status code. + + :param documents: Documents to delete from an Azure search index. May be + a single list of documents, or documents as individual parameters. + :type documents: dict or list[dict] + """ + self._extend_batch(flatten_args(documents), "delete") + + def add_merge_documents(self, *documents): + # type (Union[List[dict], List[List[dict]]]) -> None + """Add documents to merge in to existing documets in the Azure search + index. + + Merge updates an existing document with the specified fields. If the + document doesn't exist, the merge will fail. Any field you specify in a + merge will replace the existing field in the document. This also applies + to collections of primitive and complex types. + + :param documents: Documents to merge into an Azure search index. May be + a single list of documents, or documents as individual parameters. + :type documents: dict or list[dict] + """ + self._extend_batch(flatten_args(documents), "merge") + + def add_merge_or_upload_documents(self, *documents): + # type (Union[List[dict], List[List[dict]]]) -> None + """Add documents to merge in to existing documets in the Azure search + index, or upload if they do not yet exist. + + This action behaves like *merge* if a document with the given key + already exists in the index. If the document does not exist, it behaves + like *upload* with a new document. + + :param documents: Documents to merge or uplaod into an Azure search + index. May be a single list of documents, or documents as individual + parameters. + :type documents: dict or list[dict] + """ + self._extend_batch(flatten_args(documents), "mergeOrUpload") + + @property + def actions(self): + # type: () -> List[IndexAction] + """The list of currently configured index actions. + + :rtype: List[IndexAction] + """ + return list(self._actions) + + def _extend_batch(self, documents, action_type): + # type: (List[dict], str) -> None + new_actions = [ + IndexAction(additional_properties=document, action_type=action_type) + for document in documents + ] + self._actions.extend(new_actions) diff --git a/sdk/search/azure-search/azure/search/_index/_queries.py b/sdk/search/azure-search/azure/search/_index/_queries.py new file mode 100644 index 000000000000..d47ef3bb9f28 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_queries.py @@ -0,0 +1,101 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING + +from ._generated.models import AutocompleteRequest, SearchRequest, SuggestRequest + +if TYPE_CHECKING: + # pylint:disable=unused-import + from typing import Any, List, Type, Union + + +class _QueryBase(object): + + _request_type = ( + None + ) # type: Union[Type[AutocompleteRequest], Type[SearchRequest], Type[SuggestRequest]] + + def __init__(self, **kwargs): + # type: (**Any) -> None + self._request = self._request_type(**kwargs) # pylint:disable=not-callable + + def __repr__(self): + # type: () -> str + return "<{} [{}]>".format(self.__class__.__name__, self._request.search_text)[ + :1024 + ] + + def filter(self, expression): + # type: (str) -> None + """Add a `filter` expression for the search results. + + :param expression: An ODate expression of for the query filter. + :type expression: str + """ + self._request.filter = expression + + @property + def request(self): + """The service request for this operation. + + """ + return self._request + + +class AutocompleteQuery(_QueryBase): + """Represent an autocomplete query again an Azure Search index. + + """ + + _request_type = AutocompleteRequest + + __doc__ = AutocompleteRequest.__doc__ + + +class SearchQuery(_QueryBase): + """Represent a rich search query again an Azure Search index. + + """ + + _request_type = SearchRequest + + __doc__ = SearchRequest.__doc__ + + def order_by(self, *fields): + # type: (*str) -> None + """Update the `orderby` property for the search results. + + :param fields: An list of fields for the query result to be ordered by. + :type expression: str + :raises: ValueError + """ + if not fields: + raise ValueError("At least one field must be provided") + + self._request.order_by = ",".join(fields) + + def select(self, *fields): + # type: (*str) -> None + """Update the `select` property for the search results. + + :param fields: An list of fields for the query result to return. + :type expression: str + :raises: ValueError + """ + if not fields: + raise ValueError("At least one field must be provided") + + self._request.select = ",".join(fields) + + +class SuggestQuery(_QueryBase): + """Represent a search suggestion query again an Azure Search index. + + """ + + _request_type = SuggestRequest + + __doc__ = SuggestRequest.__doc__ diff --git a/sdk/search/azure-search/azure/search/_index/_search_index_client.py b/sdk/search/azure-search/azure/search/_index/_search_index_client.py new file mode 100644 index 000000000000..e3f98fc037b1 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/_search_index_client.py @@ -0,0 +1,407 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import cast, List, TYPE_CHECKING + +import base64 +import json +import six + +from azure.core.paging import ItemPaged, PageIterator +from azure.core.pipeline.policies import HeadersPolicy +from azure.core.tracing.decorator import distributed_trace +from ._generated import SearchIndexClient as _SearchIndexClient +from ._generated.models import IndexBatch, IndexingResult, SearchRequest +from ._index_documents_batch import IndexDocumentsBatch +from ._queries import AutocompleteQuery, SearchQuery, SuggestQuery + +if TYPE_CHECKING: + # pylint:disable=unused-import,ungrouped-imports + from typing import Any, Union + from ._credential import SearchApiKeyCredential + + +def odata(statement, **kwargs): + """Escape an OData query string. + + The statement to prepare should include fields to substitute given inside + braces, e.g. `{somevar}` and then pass the corresponing value as a keyword + argument, e.g. `somevar=10`. + + :param statement: An OData query string to prepare + :type statement: str + :rtype: str + + .. admonition:: Example: + + >>> odata("name eq {name} and age eq {age}", name="O'Neil", age=37) + "name eq 'O''Neil' and age eq 37" + + + """ + kw = dict(kwargs) + for key in kw: + value = kw[key] + if isinstance(value, six.string_types): + value = value.replace("'", "''") + if "'{{{}}}'".format(key) not in statement: + kw[key] = "'{}'".format(value) + return statement.format(**kw) + + +def convert_search_result(result): + ret = result.additional_properties + ret["@search.score"] = result.score + ret["@search.highlights"] = result.highlights + return ret + + +def pack_continuation_token(response): + if response.next_page_parameters is not None: + return base64.b64encode( + json.dumps( + [response.next_link, response.next_page_parameters.serialize()] + ).encode("utf-8") + ) + return None + + +def unpack_continuation_token(token): + next_link, next_page_parameters = json.loads(base64.b64decode(token)) + next_page_request = SearchRequest.deserialize(next_page_parameters) + return next_link, next_page_request + + +class _SearchDocumentsPaged(PageIterator): + def __init__(self, client, initial_query, kwargs, continuation_token=None): + super(_SearchDocumentsPaged, self).__init__( + get_next=self._get_next_cb, + extract_data=self._extract_data_cb, + continuation_token=continuation_token, + ) + self._client = client + self._initial_query = initial_query + self._kwargs = kwargs + + def _get_next_cb(self, continuation_token): + if continuation_token is None: + return self._client.documents.search_post( + search_request=self._initial_query.request, **self._kwargs + ) + + _next_link, next_page_request = unpack_continuation_token(continuation_token) + + return self._client.documents.search_post(search_request=next_page_request) + + def _extract_data_cb(self, response): # pylint:disable=no-self-use + continuation_token = pack_continuation_token(response) + + results = [convert_search_result(r) for r in response.results] + + return continuation_token, results + + +class SearchIndexClient(object): + """A client to interact with an existing Azure search index. + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_authentication.py + :start-after: [START create_search_client_with_key] + :end-before: [END create_search_client_with_key] + :language: python + :dedent: 4 + :caption: Creating the SearchIndexClient with an API key. + """ + + def __init__(self, endpoint, index_name, credential, **kwargs): + # type: (str, str, SearchApiKeyCredential, **Any) -> None + + headers_policy = HeadersPolicy( + { + "api-key": credential.api_key, + "Accept": "application/json;odata.metadata=none", + } + ) + + self._endpoint = endpoint # type: str + self._index_name = index_name # type: str + self._client = _SearchIndexClient( + endpoint=endpoint, + index_name=index_name, + headers_policy=headers_policy, + **kwargs + ) # type: _SearchIndexClient + + def __repr__(self): + # type: () -> str + return "".format( + repr(self._endpoint), repr(self._index_name) + )[:1024] + + def close(self): + # type: () -> None + """Close the :class:`~azure.search.SearchIndexClient` session. + + """ + return self._client.close() + + @distributed_trace + def get_document_count(self, **kwargs): + # type: (**Any) -> int + """Return the number of documents in the Azure search index. + + :rtype: int + """ + return int(self._client.documents.count(**kwargs)) + + @distributed_trace + def get_document(self, key, selected_fields=None, **kwargs): + # type: (str, List[str], **Any) -> dict + """Retrieve a document from the Azure search index by its key. + + :param key: The primary key value for the document to retrieve + :type key: str + :param selected_fields: a whitelist of fields to include in the results + :type selected_fields: List[str] + :rtype: dict + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_get_document.py + :start-after: [START get_document] + :end-before: [END get_document] + :language: python + :dedent: 4 + :caption: Get a specific document from the search index. + """ + result = self._client.documents.get( + key=key, selected_fields=selected_fields, **kwargs + ) + return cast(dict, result) + + @distributed_trace + def search(self, query, **kwargs): + # type: (Union[str, SearchQuery], **Any) -> ItemPaged[dict] + """Search the Azure search index for documents. + + :param query: An query for searching the index + :type documents: str or SearchQuery + :rtype: Iterable[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_simple_query.py + :start-after: [START simple_query] + :end-before: [END simple_query] + :language: python + :dedent: 4 + :caption: Search on a simple text term. + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_filter_query.py + :start-after: [START filter_query] + :end-before: [END filter_query] + :language: python + :dedent: 4 + :caption: Filter and sort search results. + """ + if isinstance(query, six.string_types): + query = SearchQuery(search_text=query) + elif not isinstance(query, SearchQuery): + raise TypeError( + "Expected a string or SearchQuery for 'query', but got {}".format( + repr(query) + ) + ) + + return ItemPaged( + self._client, query, kwargs, page_iterator_class=_SearchDocumentsPaged + ) + + @distributed_trace + def suggest(self, query, **kwargs): + # type: (SuggestQuery, **Any) -> List[dict] + """Get search suggestion results from the Azure search index. + + :param query: An query for search suggestions + :type documents: SuggestQuery + :rtype: List[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_suggestions.py + :start-after: [START suggest_query] + :end-before: [END suggest_query] + :language: python + :dedent: 4 + :caption: Get search suggestions. + """ + if not isinstance(query, SuggestQuery): + raise TypeError( + "Expected a SuggestQuery for 'query', but got {}".format(repr(query)) + ) + + response = self._client.documents.suggest_post( + suggest_request=query.request, **kwargs + ) + results = [r.as_dict() for r in response.results] + return results + + @distributed_trace + def autocomplete(self, query, **kwargs): + # type: (AutocompleteQuery, **Any) -> List[dict] + """Get search auto-completion results from the Azure search index. + + :param query: An query for auto-completions + :type documents: AutocompleteQuery + :rtype: List[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_autocomplete.py + :start-after: [START autocomplete_query] + :end-before: [END autocomplete_query] + :language: python + :dedent: 4 + :caption: Get a auto-completions. + """ + if not isinstance(query, AutocompleteQuery): + raise TypeError( + "Expected a AutocompleteQuery for 'query', but got {}".format( + repr(query) + ) + ) + + response = self._client.documents.autocomplete_post( + autocomplete_request=query.request, **kwargs + ) + results = [r.as_dict() for r in response.results] + return results + + def upload_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Upload documents to the Azure search index. + + An upload action is similar to an "upsert" where the document will be + inserted if it is new and updated/replaced if it exists. All fields are + replaced in the update case. + + :param documents: A list of documents to upload. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_crud_operations.py + :start-after: [START upload_document] + :end-before: [END upload_document] + :language: python + :dedent: 4 + :caption: Upload new documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_upload_documents(documents) + results = self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + def delete_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Delete documents from the Azure search index + + Delete removes the specified document from the index. Any field you + specify in a delete operation, other than the key field, will be + ignored. If you want to remove an individual field from a document, use + `merge_documents` instead and set the field explicitly to None. + + Delete operations are idempotent. That is, even if a document key does + not exist in the index, attempting a delete operation with that key will + result in a 200 status code. + + :param documents: A list of documents to delete. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_crud_operations.py + :start-after: [START delete_document] + :end-before: [END delete_document] + :language: python + :dedent: 4 + :caption: Delete existing documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_delete_documents(documents) + results = self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + def merge_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Merge documents in to existing documents in the Azure search index. + + Merge updates an existing document with the specified fields. If the + document doesn't exist, the merge will fail. Any field you specify in a + merge will replace the existing field in the document. This also applies + to collections of primitive and complex types. + + :param documents: A list of documents to merge. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/sample_crud_operations.py + :start-after: [START merge_document] + :end-before: [END merge_document] + :language: python + :dedent: 4 + :caption: Merge fields into existing documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_merge_documents(documents) + results = self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + def merge_or_upload_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Merge documents in to existing documents in the Azure search index, + or upload them if they do not yet exist. + + This action behaves like `merge_documents` if a document with the given + key already exists in the index. If the document does not exist, it + behaves like `upload_documents` with a new document. + + :param documents: A list of documents to merge or upload. + :type documents: List[dict] + :rtype: List[IndexingResult] + """ + batch = IndexDocumentsBatch() + batch.add_merge_or_upload_documents(documents) + results = self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + @distributed_trace + def index_documents(self, batch, **kwargs): + # type: (IndexDocumentsBatch, **Any) -> List[IndexingResult] + """Specify a document operations to perform as a batch. + + :param batch: A batch of document operations to perform. + :type batch: IndexDocumentsBatch + :rtype: List[IndexingResult] + """ + index_documents = IndexBatch(actions=batch.actions) + batch_response = self._client.documents.index(batch=index_documents, **kwargs) + return cast(List[IndexingResult], batch_response.results) + + def __enter__(self): + # type: () -> SearchIndexClient + self._client.__enter__() # pylint:disable=no-member + return self + + def __exit__(self, *args): + # type: (*Any) -> None + self._client.__exit__(*args) # pylint:disable=no-member diff --git a/sdk/search/azure-search/azure/search/_index/aio/__init__.py b/sdk/search/azure-search/azure/search/_index/aio/__init__.py new file mode 100644 index 000000000000..9a68cfb37fe0 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/aio/__init__.py @@ -0,0 +1,7 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from ._search_index_client_async import SearchIndexClient + +__all__ = ("SearchIndexClient",) diff --git a/sdk/search/azure-search/azure/search/_index/aio/_search_index_client_async.py b/sdk/search/azure-search/azure/search/_index/aio/_search_index_client_async.py new file mode 100644 index 000000000000..eeccb1ce7e64 --- /dev/null +++ b/sdk/search/azure-search/azure/search/_index/aio/_search_index_client_async.py @@ -0,0 +1,364 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +from typing import cast, List, TYPE_CHECKING + +import six + +from azure.core.async_paging import AsyncItemPaged, AsyncPageIterator +from azure.core.pipeline.policies import HeadersPolicy +from azure.core.tracing.decorator_async import distributed_trace_async +from .._generated.aio import SearchIndexClient as _SearchIndexClient +from .._generated.models import IndexBatch, IndexingResult, SearchRequest +from .._index_documents_batch import IndexDocumentsBatch +from .._queries import AutocompleteQuery, SearchQuery, SuggestQuery +from .._search_index_client import ( + convert_search_result, + pack_continuation_token, + unpack_continuation_token, +) + +if TYPE_CHECKING: + # pylint:disable=unused-import,ungrouped-imports + from typing import Any, Union + from .._credential import SearchApiKeyCredential + + +class _SearchDocumentsPagedAsync(AsyncPageIterator): + def __init__(self, client, initial_query, kwargs, continuation_token=None): + super(_SearchDocumentsPagedAsync, self).__init__( + get_next=self._get_next_cb, + extract_data=self._extract_data_cb, + continuation_token=continuation_token, + ) + self._client = client + self._initial_query = initial_query + self._kwargs = kwargs + + async def _get_next_cb(self, continuation_token): + if continuation_token is None: + return await self._client.documents.search_post( + search_request=self._initial_query.request, **self._kwargs + ) + + _next_link, next_page_request = unpack_continuation_token(continuation_token) + + return await self._client.documents.search_post( + search_request=next_page_request + ) + + async def _extract_data_cb(self, response): # pylint:disable=no-self-use + continuation_token = pack_continuation_token(response) + + results = [convert_search_result(r) for r in response.results] + + return continuation_token, results + + +class SearchIndexClient(object): + """A client to interact with an existing Azure search index. + + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_authentication_async.py + :start-after: [START create_search_client_with_key_async] + :end-before: [END create_search_client_with_key_async] + :language: python + :dedent: 4 + :caption: Creating the SearchIndexClient with an API key. + """ + + def __init__(self, endpoint, index_name, credential, **kwargs): + # type: (str, str, SearchApiKeyCredential, **Any) -> None + + headers_policy = HeadersPolicy( + { + "api-key": credential.api_key, + "Accept": "application/json;odata.metadata=none", + } + ) + + self._endpoint = endpoint # type: str + self._index_name = index_name # type: str + self._client = _SearchIndexClient( + endpoint=endpoint, + index_name=index_name, + headers_policy=headers_policy, + **kwargs + ) # type: _SearchIndexClient + + def __repr__(self): + # type: () -> str + return "".format( + repr(self._endpoint), repr(self._index_name) + )[:1024] + + async def close(self): + # type: () -> None + """Close the :class:`~azure.search.aio.SearchIndexClient` session. + + """ + return await self._client.close() + + @distributed_trace_async + async def get_document_count(self, **kwargs): + # type: (**Any) -> int + """Return the number of documents in the Azure search index. + + :rtype: int + """ + return int(await self._client.documents.count(**kwargs)) + + @distributed_trace_async + async def get_document(self, key, selected_fields=None, **kwargs): + # type: (str, List[str], **Any) -> dict + """Retrieve a document from the Azure search index by its key. + + :param key: The primary key value for the document to retrieve + :type key: str + :param selected_fields: a whitelist of fields to include in the results + :type selected_fields: List[str] + :rtype: dict + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_get_document_async.py + :start-after: [START get_document_async] + :end-before: [END get_document_async] + :language: python + :dedent: 4 + :caption: Get a specific document from the search index. + """ + result = await self._client.documents.get( + key=key, selected_fields=selected_fields, **kwargs + ) + return cast(dict, result) + + @distributed_trace_async + async def search(self, query, **kwargs): + # type: (Union[str, SearchQuery], **Any) -> AsyncItemPaged[dict] + """Search the Azure search index for documents. + + :param query: An query for searching the index + :type documents: str or SearchQuery + :rtype: Iterable[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_simple_query_async.py + :start-after: [START simple_query_async] + :end-before: [END simple_query_async] + :language: python + :dedent: 4 + :caption: Search on a simple text term. + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_filter_query_async.py + :start-after: [START filter_query_async] + :end-before: [END filter_query_async] + :language: python + :dedent: 4 + :caption: Filter and sort search results. + """ + if isinstance(query, six.string_types): + query = SearchQuery(search_text=query) + elif not isinstance(query, SearchQuery): + raise TypeError( + "Expected a string or SearchQuery for 'query', but got {}".format( + repr(query) + ) + ) + + return AsyncItemPaged( + self._client, query, kwargs, page_iterator_class=_SearchDocumentsPagedAsync + ) + + @distributed_trace_async + async def suggest(self, query, **kwargs): + # type: (Union[str, SuggestQuery], **Any) -> List[dict] + """Get search suggestion results from the Azure search index. + + :param query: An query for search suggestions + :type documents: SuggestQuery + :rtype: List[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_suggestions_async.py + :start-after: [START suggest_query_async] + :end-before: [END suggest_query_async] + :language: python + :dedent: 4 + :caption: Get search suggestions. + """ + if not isinstance(query, SuggestQuery): + raise TypeError( + "Expected a SuggestQuery for 'query', but got {}".format(repr(query)) + ) + + response = await self._client.documents.suggest_post( + suggest_request=query.request, **kwargs + ) + results = [r.as_dict() for r in response.results] + return results + + @distributed_trace_async + async def autocomplete(self, query, **kwargs): + # type: (Union[str, AutocompleteQuery], **Any) -> List[dict] + """Get search auto-completion results from the Azure search index. + + :param query: An query for auto-completions + :type documents: AutocompleteQuery + :rtype: List[dict] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_autocomplete_async.py + :start-after: [START autocomplete_query_async] + :end-before: [END autocomplete_query_async] + :language: python + :dedent: 4 + :caption: Get a auto-completions. + """ + if not isinstance(query, AutocompleteQuery): + raise TypeError( + "Expected a AutocompleteQuery for 'query', but got {}".format( + repr(query) + ) + ) + + response = await self._client.documents.autocomplete_post( + autocomplete_request=query.request, **kwargs + ) + results = [r.as_dict() for r in response.results] + return results + + async def upload_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Upload documents to the Azure search index. + + An upload action is similar to an "upsert" where the document will be + inserted if it is new and updated/replaced if it exists. All fields are + replaced in the update case. + + :param documents: A list of documents to upload. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_crud_operations_async.py + :start-after: [START upload_document_async] + :end-before: [END upload_document_async] + :language: python + :dedent: 4 + :caption: Upload new documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_upload_documents(documents) + results = await self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + async def delete_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Delete documents from the Azure search index + + Delete removes the specified document from the index. Any field you + specify in a delete operation, other than the key field, will be + ignored. If you want to remove an individual field from a document, use + `merge_documents` instead and set the field explicitly to None. + + Delete operations are idempotent. That is, even if a document key does + not exist in the index, attempting a delete operation with that key will + result in a 200 status code. + + :param documents: A list of documents to delete. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_crud_operations_async.py + :start-after: [START delete_document_async] + :end-before: [END delete_document_async] + :language: python + :dedent: 4 + :caption: Delete existing documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_delete_documents(documents) + results = await self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + async def merge_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Merge documents in to existing documents in the Azure search index. + + Merge updates an existing document with the specified fields. If the + document doesn't exist, the merge will fail. Any field you specify in a + merge will replace the existing field in the document. This also applies + to collections of primitive and complex types. + + :param documents: A list of documents to merge. + :type documents: List[dict] + :rtype: List[IndexingResult] + + .. admonition:: Example: + + .. literalinclude:: ../samples/async_samples/sample_crud_operations_async.py + :start-after: [START merge_document_async] + :end-before: [END merge_document_async] + :language: python + :dedent: 4 + :caption: Merge fields into existing documents to an index + """ + batch = IndexDocumentsBatch() + batch.add_merge_documents(documents) + results = await self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + async def merge_or_upload_documents(self, documents, **kwargs): + # type: (List[dict], **Any) -> List[IndexingResult] + """Merge documents in to existing documents in the Azure search index, + or upload them if they do not yet exist. + + This action behaves like `merge_documents` if a document with the given + key already exists in the index. If the document does not exist, it + behaves like `upload_documents` with a new document. + + :param documents: A list of documents to merge or upload. + :type documents: List[dict] + :rtype: List[IndexingResult] + """ + batch = IndexDocumentsBatch() + batch.add_merge_or_upload_documents(documents) + results = await self.index_documents(batch, **kwargs) + return cast(List[IndexingResult], results) + + @distributed_trace_async + async def index_documents(self, batch, **kwargs): + # type: (IndexDocumentsBatch, **Any) -> List[IndexingResult] + """Specify a document operations to perform as a batch. + + :param batch: A batch of document operations to perform. + :type batch: IndexDocumentsBatch + :rtype: List[IndexingResult] + """ + index_documents = IndexBatch(actions=batch.actions) + batch_response = await self._client.documents.index( + batch=index_documents, **kwargs + ) + return cast(List[IndexingResult], batch_response.results) + + async def __aenter__(self): + # type: () -> SearchIndexClient + await self._client.__aenter__() # pylint: disable=no-member + return self + + async def __aexit__(self, *args): + # type: (*Any) -> None + await self._client.__aexit__(*args) # pylint: disable=no-member diff --git a/sdk/search/azure-search/azure/search/_version.py b/sdk/search/azure-search/azure/search/_version.py new file mode 100644 index 000000000000..224de1f49bac --- /dev/null +++ b/sdk/search/azure-search/azure/search/_version.py @@ -0,0 +1,6 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +VERSION = "11.0.0b1" diff --git a/sdk/search/azure-search/azure/search/aio.py b/sdk/search/azure-search/azure/search/aio.py new file mode 100644 index 000000000000..abed3a65e518 --- /dev/null +++ b/sdk/search/azure-search/azure/search/aio.py @@ -0,0 +1,29 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +from ._index.aio import SearchIndexClient + +__all__ = ("SearchIndexClient",) diff --git a/sdk/search/azure-search/dev_requirements.txt b/sdk/search/azure-search/dev_requirements.txt new file mode 100644 index 000000000000..32a21095f8bb --- /dev/null +++ b/sdk/search/azure-search/dev_requirements.txt @@ -0,0 +1,5 @@ +-e ../../../tools/azure-devtools +-e ../../../tools/azure-sdk-tools +../../core/azure-core +aiohttp>=3.0; python_version >= '3.5' +pytest diff --git a/sdk/search/azure-search/mypy.ini b/sdk/search/azure-search/mypy.ini new file mode 100644 index 000000000000..9a87276018b6 --- /dev/null +++ b/sdk/search/azure-search/mypy.ini @@ -0,0 +1,15 @@ +[mypy] +python_version = 3.6 +warn_return_any = True +warn_unused_configs = True +ignore_missing_imports = True + +# Per-module options: + +[mypy-azure.search.index._generated.*] +ignore_errors = True +follow_imports = skip + +[mypy-azure.core.*] +ignore_errors = True + diff --git a/sdk/search/azure-search/samples/README.md b/sdk/search/azure-search/samples/README.md new file mode 100644 index 000000000000..fedb9ddb0572 --- /dev/null +++ b/sdk/search/azure-search/samples/README.md @@ -0,0 +1,61 @@ +--- +topic: sample +languages: + - python +products: + - azure + - azure-search +--- + +# Samples for Azure Cognitive Search client library for Python + +These code samples show common scenario operations with the Azure Cognitive +Search client library. The async versions of the samples (the python sample +files appended with `_async`) show asynchronous operations with Cognitive Search +and require Python version 3.5 or later. + +Authenticate the client with a Azure Cognitive Search [API Key Credential](https://docs.microsoft.com/en-us/azure/search/search-security-api-keys): + +[sample_authentication.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_authentication.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/iasync_samples/sample_authentication_async.py)) + +Then for common search index operations: + +* Get a document by key: [sample_get_document.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_get_document.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_get_document_async.py)) + +* Perform a simple text query: [sample_simple_query.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_simple_query.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_simple_query_async.py)) + +* Perform a filtered query: [sample_filter_query.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_filter_query.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_filter_query_async.py)) + +* Get auto-completions: [sample_autocomplete.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_autocomplete.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_autocomplete_async.py)) + +* Get search suggestions: [sample_suggestions.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_suggestions.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_suggestions_async.py)) + +* Perform basic document updates: [sample_crud_operations.py](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/sample_crud_operations.py) ([async version](https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search/samples/async_samples/sample_crud_operations_async.py)) + +## Prerequisites +* Python 2.7, or 3.5 or later is required to use this package (3.5 or later if using asyncio) +* You must have an [Azure subscription](https://azure.microsoft.com/free/) +* You must create the "Hotels" sample index [in the Azure Portal](https://docs.microsoft.com/en-us/azure/search/search-get-started-portal) + + +## Setup + +1. Install the Azure Cognitive Search client library for Python with [pip](https://pypi.org/project/pip/): + + ```bash + pip install azure-search --pre + ``` + +2. Clone or download [this repository](https://github.com/Azure/azure-sdk-for-python) +3. Open this sample folder in [Visual Studio Code](https://code.visualstudio.com) or your IDE of choice. + +## Running the samples + +1. Open a terminal window and `cd` to the directory that the samples are saved in. +2. Set the environment variables specified in the sample file you wish to run. +3. Follow the usage described in the file, e.g. `python sample_simple_query.py` + +## Next steps + +Check out the [API reference documentation](https://docs.microsoft.com/en-us/rest/api/searchservice/) +to learn more about what you can do with the Azure Cognitive Search client library. diff --git a/sdk/search/azure-search/samples/async_samples/sample_authentication_async.py b/sdk/search/azure-search/samples/async_samples/sample_authentication_async.py new file mode 100644 index 000000000000..39cc7637ecef --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_authentication_async.py @@ -0,0 +1,44 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_authentication.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_authentication.py + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import asyncio +import os + +async def authentication_with_api_key_credential_async(): + # [START create_search_client_with_key_async] + from azure.search.aio import SearchIndexClient + from azure.search import SearchApiKeyCredential + service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") + index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") + key = os.getenv("AZURE_SEARCH_API_KEY") + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + # [END create_search_client_with_key_async] + + async with search_client: + result = await search_client.get_document_count() + + print("There are {} documents in the {} search index.".format(result, repr(index_name))) + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(authentication_with_api_key_credential_async()) \ No newline at end of file diff --git a/sdk/search/azure-search/samples/async_samples/sample_autocomplete_async.py b/sdk/search/azure-search/samples/async_samples/sample_autocomplete_async.py new file mode 100644 index 000000000000..b7ad2acef5e9 --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_autocomplete_async.py @@ -0,0 +1,52 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_autocomplete_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_autocomplete_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +async def autocomplete_query(): + # [START autocomplete_query_async] + from azure.search.aio import SearchIndexClient + from azure.search import AutocompleteQuery, SearchApiKeyCredential + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = AutocompleteQuery(search_text="bo", suggester_name="sg") + + results = await search_client.autocomplete(query=query) + + print("Autocomplete suggestions for 'bo'") + for result in results: + print(" Completion: {}".format(result["text"])) + + await search_client.close() + # [END autocomplete_query_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(autocomplete_query()) \ No newline at end of file diff --git a/sdk/search/azure-search/samples/async_samples/sample_crud_operations_async.py b/sdk/search/azure-search/samples/async_samples/sample_crud_operations_async.py new file mode 100644 index 000000000000..508fd099c162 --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_crud_operations_async.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_crud_operations_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_crud_operations_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +from azure.search.aio import SearchIndexClient +from azure.search import SearchApiKeyCredential, SearchQuery + +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +async def upload_document(): + # [START upload_document_async] + DOCUMENT = { + 'Category': 'Hotel', + 'HotelId': '1000', + 'Rating': 4.0, + 'Rooms': [], + 'HotelName': 'Azure Inn', + } + + result = await search_client.upload_documents(documents=[DOCUMENT]) + + print("Upload of new document succeeded: {}".format(result[0].succeeded)) + # [END upload_document_async] + +async def merge_document(): + # [START merge_document_async] + result = await search_client.upload_documents(documents=[{"HotelId": "1000", "Rating": 4.5}]) + + print("Merge into new document succeeded: {}".format(result[0].succeeded)) + # [END merge_document_async] + +async def delete_document(): + # [START delete_document_async] + result = await search_client.upload_documents(documents=[{"HotelId": "1000"}]) + + print("Delete new document succeeded: {}".format(result[0].succeeded)) + # [END delete_document_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(asyncio.gather( + upload_document(), + merge_document(), + delete_document(), + )) + asyncio.run(search_client.close()) + diff --git a/sdk/search/azure-search/samples/async_samples/sample_filter_query_async.py b/sdk/search/azure-search/samples/async_samples/sample_filter_query_async.py new file mode 100644 index 000000000000..5157131374bc --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_filter_query_async.py @@ -0,0 +1,55 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_filter_query_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_filter_query_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +async def filter_query(): + # [START filter_query_async] + from azure.search.aio import SearchIndexClient + from azure.search import SearchApiKeyCredential, SearchQuery + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = SearchQuery(search_text="WiFi") + query.filter("Address/StateProvince eq 'FL' and Address/Country eq 'USA'") + query.select("HotelName", "Rating") + query.order_by("Rating desc") + + results = await search_client.search(query=query) + + print("Florida hotels containing 'WiFi', sorted by Rating:") + async for result in results: + print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) + + await search_client.close() + # [END filter_query_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(filter_query()) \ No newline at end of file diff --git a/sdk/search/azure-search/samples/async_samples/sample_get_document_async.py b/sdk/search/azure-search/samples/async_samples/sample_get_document_async.py new file mode 100644 index 000000000000..925d3a3e764f --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_get_document_async.py @@ -0,0 +1,51 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_get_document_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_get_document_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +async def autocomplete_query(): + # [START get_document_async] + from azure.search.aio import SearchIndexClient + from azure.search import SearchApiKeyCredential + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + result = await search_client.get_document(key="23") + + print("Details for hotel '23' are:") + print(" Name: {}".format(result["HotelName"])) + print(" Rating: {}".format(result["Rating"])) + print(" Category: {}".format(result["Category"])) + + await search_client.close() + # [END get_document_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(autocomplete_query()) \ No newline at end of file diff --git a/sdk/search/azure-search/samples/async_samples/sample_simple_query_async.py b/sdk/search/azure-search/samples/async_samples/sample_simple_query_async.py new file mode 100644 index 000000000000..69794734f1a9 --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_simple_query_async.py @@ -0,0 +1,50 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_simple_query_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_simple_query_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +async def simple_text_query(): + # [START simple_query_async] + from azure.search.aio import SearchIndexClient + from azure.search import SearchApiKeyCredential + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + results = await search_client.search(query="spa") + + print("Hotels containing 'spa' in the name (or other fields):") + async for result in results: + print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) + + await search_client.close() + # [END simple_query_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(simple_text_query()) \ No newline at end of file diff --git a/sdk/search/azure-search/samples/async_samples/sample_suggestions_async.py b/sdk/search/azure-search/samples/async_samples/sample_suggestions_async.py new file mode 100644 index 000000000000..046869bd32e7 --- /dev/null +++ b/sdk/search/azure-search/samples/async_samples/sample_suggestions_async.py @@ -0,0 +1,53 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_suggestions_async.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_suggestions_async.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os +import asyncio + + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +async def suggest_query(): + # [START suggest_query_async] + from azure.search.aio import SearchIndexClient + from azure.search import SearchApiKeyCredential, SuggestQuery + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = SuggestQuery(search_text="coffee", suggester_name="sg") + + results = await search_client.suggest(query=query) + + print("Search suggestions for 'coffee'") + for result in results: + hotel = await search_client.get_document(key=result["HotelId"]) + print(" Text: {} for Hotel: {}".format(repr(result["text"]), hotel["HotelName"])) + + await search_client.close() + # [END suggest_query_async] + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + loop.run_until_complete(suggest_query()) diff --git a/sdk/search/azure-search/samples/sample_authentication.py b/sdk/search/azure-search/samples/sample_authentication.py new file mode 100644 index 000000000000..bc64d3d99da3 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_authentication.py @@ -0,0 +1,41 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_authentication.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_authentication.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +def authentication_with_api_key_credential(): + # [START create_search_client_with_key] + from azure.search import SearchApiKeyCredential, SearchIndexClient + service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") + index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") + key = os.getenv("AZURE_SEARCH_API_KEY") + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + # [END create_search_client_with_key] + + result = search_client.get_document_count() + + print("There are {} documents in the {} search index.".format(result, repr(index_name))) + +if __name__ == '__main__': + authentication_with_api_key_credential() \ No newline at end of file diff --git a/sdk/search/azure-search/samples/sample_autocomplete.py b/sdk/search/azure-search/samples/sample_autocomplete.py new file mode 100644 index 000000000000..6cec079b8251 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_autocomplete.py @@ -0,0 +1,46 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_autocomplete.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_autocomplete.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +def autocomplete_query(): + # [START autocomplete_query] + from azure.search import AutocompleteQuery, SearchApiKeyCredential, SearchIndexClient + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = AutocompleteQuery(search_text="bo", suggester_name="sg") + + results = search_client.autocomplete(query=query) + + print("Autocomplete suggestions for 'bo'") + for result in results: + print(" Completion: {}".format(result["text"])) + # [END autocomplete_query] + +if __name__ == '__main__': + autocomplete_query() \ No newline at end of file diff --git a/sdk/search/azure-search/samples/sample_crud_operations.py b/sdk/search/azure-search/samples/sample_crud_operations.py new file mode 100644 index 000000000000..d8ef9f505a38 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_crud_operations.py @@ -0,0 +1,65 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_crud_operations.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_crud_operations.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +from azure.search import SearchApiKeyCredential, SearchIndexClient +search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + +def upload_document(): + # [START upload_document] + DOCUMENT = { + 'Category': 'Hotel', + 'HotelId': '1000', + 'Rating': 4.0, + 'Rooms': [], + 'HotelName': 'Azure Inn', + } + + result = search_client.upload_documents(documents=[DOCUMENT]) + + print("Upload of new document succeeded: {}".format(result[0].succeeded)) + # [END upload_document] + +def merge_document(): + # [START merge_document] + result = search_client.upload_documents(documents=[{"HotelId": "1000", "Rating": 4.5}]) + + print("Merge into new document succeeded: {}".format(result[0].succeeded)) + # [END merge_document] + +def delete_document(): + # [START delete_document] + result = search_client.upload_documents(documents=[{"HotelId": "1000"}]) + + print("Delete new document succeeded: {}".format(result[0].succeeded)) + # [END delete_document] + +if __name__ == '__main__': + upload_document() + merge_document() + delete_document() diff --git a/sdk/search/azure-search/samples/sample_filter_query.py b/sdk/search/azure-search/samples/sample_filter_query.py new file mode 100644 index 000000000000..09d9096c1b39 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_filter_query.py @@ -0,0 +1,49 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_filter_query.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_filter_query.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +def filter_query(): + # [START filter_query] + from azure.search import SearchApiKeyCredential, SearchIndexClient, SearchQuery + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = SearchQuery(search_text="WiFi") + query.filter("Address/StateProvince eq 'FL' and Address/Country eq 'USA'") + query.select("HotelName", "Rating") + query.order_by("Rating desc") + + results = search_client.search(query=query) + + print("Florida hotels containing 'WiFi', sorted by Rating:") + for result in results: + print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) + # [END filter_query] + +if __name__ == '__main__': + filter_query() \ No newline at end of file diff --git a/sdk/search/azure-search/samples/sample_get_document.py b/sdk/search/azure-search/samples/sample_get_document.py new file mode 100644 index 000000000000..15e29d86df36 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_get_document.py @@ -0,0 +1,45 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_get_document.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_get_document.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +def get_document(): + # [START get_document] + from azure.search import SearchApiKeyCredential, SearchIndexClient + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + result = search_client.get_document(key="23") + + print("Details for hotel '23' are:") + print(" Name: {}".format(result["HotelName"])) + print(" Rating: {}".format(result["Rating"])) + print(" Category: {}".format(result["Category"])) + # [END get_document] + +if __name__ == '__main__': + get_document() \ No newline at end of file diff --git a/sdk/search/azure-search/samples/sample_simple_query.py b/sdk/search/azure-search/samples/sample_simple_query.py new file mode 100644 index 000000000000..8b21e20cadb8 --- /dev/null +++ b/sdk/search/azure-search/samples/sample_simple_query.py @@ -0,0 +1,44 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_simple_query.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_simple_query.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +def simple_text_query(): + # [START simple_query] + from azure.search import SearchApiKeyCredential, SearchIndexClient + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + results = search_client.search(query="spa") + + print("Hotels containing 'spa' in the name (or other fields):") + for result in results: + print(" Name: {} (rating {})".format(result["HotelName"], result["Rating"])) + # [END simple_query] + +if __name__ == '__main__': + simple_text_query() \ No newline at end of file diff --git a/sdk/search/azure-search/samples/sample_suggestions.py b/sdk/search/azure-search/samples/sample_suggestions.py new file mode 100644 index 000000000000..7ca96088f9da --- /dev/null +++ b/sdk/search/azure-search/samples/sample_suggestions.py @@ -0,0 +1,47 @@ +# coding: utf-8 + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_suggestions.py +DESCRIPTION: + This sample demonstrates how to authenticate with the Azure Congnitive Search + service with an API key. See more details about authentication here: + https://docs.microsoft.com/en-us/azure/search/search-security-api-keys +USAGE: + python sample_suggestions.py + + Set the environment variables with your own values before running the sample: + 1) AZURE_SEARCH_SERVICE_ENDPOINT - the endpoint of your Azure Cognitive Search service + 2) AZURE_SEARCH_INDEX_NAME - the name of your search index (e.g. "hotels-sample-index") + 3) AZURE_SEARCH_API_KEY - your search API key +""" + +import os + +service_endpoint = os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT") +index_name = os.getenv("AZURE_SEARCH_INDEX_NAME") +key = os.getenv("AZURE_SEARCH_API_KEY") + +def suggest_query(): + # [START suggest_query] + from azure.search import SearchApiKeyCredential, SearchIndexClient, SuggestQuery + + search_client = SearchIndexClient(service_endpoint, index_name, SearchApiKeyCredential(key)) + + query = SuggestQuery(search_text="coffee", suggester_name="sg") + + results = search_client.suggest(query=query) + + print("Search suggestions for 'coffee'") + for result in results: + hotel = search_client.get_document(key=result["HotelId"]) + print(" Text: {} for Hotel: {}".format(repr(result["text"]), hotel["HotelName"])) + # [END suggest_query] + +if __name__ == '__main__': + suggest_query() diff --git a/sdk/search/azure-search/sdk_packaging.toml b/sdk/search/azure-search/sdk_packaging.toml new file mode 100644 index 000000000000..e7687fdae93b --- /dev/null +++ b/sdk/search/azure-search/sdk_packaging.toml @@ -0,0 +1,2 @@ +[packaging] +auto_update = false \ No newline at end of file diff --git a/sdk/search/azure-search/setup.cfg b/sdk/search/azure-search/setup.cfg new file mode 100644 index 000000000000..3c6e79cf31da --- /dev/null +++ b/sdk/search/azure-search/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/sdk/search/azure-search/setup.py b/sdk/search/azure-search/setup.py new file mode 100644 index 000000000000..152050079715 --- /dev/null +++ b/sdk/search/azure-search/setup.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- + + +import os +import re + +from setuptools import setup, find_packages + +# Change the PACKAGE_NAME only to change folder and different name +PACKAGE_NAME = "azure-search" +PACKAGE_PPRINT_NAME = "Azure Cognitive Search" + +# a-b-c => a/b/c +PACKAGE_FOLDER_PATH = PACKAGE_NAME.replace("-", "/") +# a-b-c => a.b.c +NAMESPACE_NAME = PACKAGE_NAME.replace("-", ".") + +# azure v0.x is not compatible with this package +# azure v0.x used to have a __version__ attribute (newer versions don't) +try: + import azure + + try: + ver = azure.__version__ + raise Exception( + 'This package is incompatible with azure=={}. '.format(ver) + + 'Uninstall it with "pip uninstall azure".' + ) + except AttributeError: + pass +except ImportError: + pass + +# Version extraction inspired from 'requests' +with open(os.path.join(PACKAGE_FOLDER_PATH, '_version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) + +if not version: + raise RuntimeError('Cannot find version information') + +setup( + name=PACKAGE_NAME, + version=version, + description='Microsoft {} Client Library for Python'.format(PACKAGE_PPRINT_NAME), + long_description=open('README.md', 'r').read(), + long_description_content_type='text/markdown', + license='MIT License', + author='Microsoft Corporation', + author_email='ascl@microsoft.com', + url='https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/search/azure-search', + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: MIT License', + ], + zip_safe=False, + packages=find_packages(exclude=[ + 'samples', + 'tests', + # Exclude packages that will be covered by PEP420 or nspkg + 'azure', + ]), + install_requires=[ + "azure-core<2.0.0,>=1.2.2", + "msrest>=0.6.10", + ], + extras_require={ + ":python_version<'3.0'": ['futures', 'azure-nspkg'], + ":python_version<'3.4'": ['enum34>=1.0.4'], + ":python_version<'3.5'": ["typing"] + }, +) diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_async_get_document_count.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_async_get_document_count.yaml new file mode 100644 index 000000000000..26e9154b49f6 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_async_get_document_count.yaml @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 5E3CCE376325571C0E17AE2DC623AD64 + method: GET + uri: https://searchaab2129d.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '127' + content-type: text/plain + date: Fri, 06 Mar 2020 22:22:42 GMT + elapsed-time: '112' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: ff0772ec-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaab2129d.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_autocomplete.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_autocomplete.yaml new file mode 100644 index 000000000000..1a47e4d8f197 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_autocomplete.yaml @@ -0,0 +1,38 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 50BFC418E3DB22CD3B0D09D87A994465 + method: POST + uri: https://searche84d0dac.search.windows.net/indexes('drgqefsg')/docs/search.post.autocomplete?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"text":"motel","queryPlusText":"motel"}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '163' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:22:53 GMT + elapsed-time: '134' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 05d69346-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche84d0dac.search.windows.net/indexes('drgqefsg')/docs/search.post.autocomplete?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_existing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_existing.yaml new file mode 100644 index 000000000000..f42577c56dd3 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_existing.yaml @@ -0,0 +1,125 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "3", "@search.action": "delete"}, {"hotelId": "4", + "@search.action": "delete"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '103' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A98151E557D01D60428885F3A1E6385F + method: POST + uri: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"3","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '190' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:04 GMT + elapsed-time: '76' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 0c36d3fe-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A98151E557D01D60428885F3A1E6385F + method: GET + uri: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF8" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '126' + content-type: text/plain + date: Fri, 06 Mar 2020 22:23:07 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 0e1ce74e-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A98151E557D01D60428885F3A1E6385F + method: GET + uri: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:23:07 GMT + elapsed-time: '3' + expires: '-1' + pragma: no-cache + request-id: 0e222e0c-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A98151E557D01D60428885F3A1E6385F + method: GET + uri: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:23:07 GMT + elapsed-time: '5' + expires: '-1' + pragma: no-cache + request-id: 0e274bee-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://searchbd291308.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_missing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_missing.yaml new file mode 100644 index 000000000000..426b23bfe06b --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_delete_documents_missing.yaml @@ -0,0 +1,125 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "@search.action": "delete"}, {"hotelId": + "4", "@search.action": "delete"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '106' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852F7F77AFD939BA4322E749C0A9925 + method: POST + uri: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '193' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:19 GMT + elapsed-time: '90' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 14f02fd6-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852F7F77AFD939BA4322E749C0A9925 + method: GET + uri: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF9" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '126' + content-type: text/plain + date: Fri, 06 Mar 2020 22:23:22 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 16d88578-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852F7F77AFD939BA4322E749C0A9925 + method: GET + uri: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:23:22 GMT + elapsed-time: '3' + expires: '-1' + pragma: no-cache + request-id: 16ddf454-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852F7F77AFD939BA4322E749C0A9925 + method: GET + uri: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:23:22 GMT + elapsed-time: '5' + expires: '-1' + pragma: no-cache + request-id: 16e309bc-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://searchaa131297.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document.yaml new file mode 100644 index 000000000000..b06f0f4785e8 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document.yaml @@ -0,0 +1,370 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('1')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1","hotelName":"Fancy Stay","description":"Best hotel in + town if you like luxury hotels. They have an amazing infinity pool, a spa, + and a really helpful concierge. The location is perfect -- right downtown, + close to all the tourist attractions. We highly recommend this hotel.","descriptionFr":"Meilleur + h\u00f4tel en ville si vous aimez les h\u00f4tels de luxe. Ils ont une magnifique + piscine \u00e0 d\u00e9bordement, un spa et un concierge tr\u00e8s utile. L''emplacement + est parfait \u2013 en plein centre, \u00e0 proximit\u00e9 de toutes les attractions + touristiques. Nous recommandons fortement cet h\u00f4tel.","category":"Luxury","tags":["pool","view","wifi","concierge"],"parkingIncluded":false,"smokingAllowed":false,"lastRenovationDate":"2010-06-27T00:00:00Z","rating":5,"location":{"type":"Point","coordinates":[-122.131577,47.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '748' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '72' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1d7c1fa2-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('1')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('2')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"2","hotelName":"Roach Motel","description":"Cheapest hotel + in town. Infact, a motel.","descriptionFr":"H\u00f4tel le moins cher en ville. + Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '449' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1d97e642-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('2')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"3","hotelName":"EconoStay","description":"Very popular + hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '438' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1d9d4bd2-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '422' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1da2b7e8-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('5')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"5","hotelName":"Comfy Place","description":"Another good + hotel","descriptionFr":"Un autre bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"2012-08-12T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '424' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '7' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1da7f8ca-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('5')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('6')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"6","hotelName":null,"description":"Surprisingly expensive. + Model suites have an ocean-view.","descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '301' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '6' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1dadba76-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('6')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('7')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"7","hotelName":"Modern Stay","description":"Modern architecture, + very polite staff and very clean. Also very affordable.","descriptionFr":"Architecture + moderne, personnel poli et tr\u00e8s propre. Aussi tr\u00e8s abordable.","category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '357' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '4' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1db318ea-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('7')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('8')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"8","hotelName":null,"description":"Has some road noise + and is next to the very police station. Bathrooms had morel coverings.","descriptionFr":"Il + y a du bruit de la route et se trouve \u00e0 c\u00f4t\u00e9 de la station + de police. Les salles de bain avaient des rev\u00eatements de morilles.","category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '411' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '4' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1db84946-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('8')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('9')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"9","hotelName":"Secret Point Motel","description":"The + hotel is ideally located on the main commercial artery of the city in the + heart of New York. A few minutes away is Time''s Square and the historic centre + of the city, as well as other places of interest that make New York one of + America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '1061' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '8' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1dbdc024-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('9')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 78CFD93C7791375C7C970B4DDE545020 + method: GET + uri: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('10')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"10","hotelName":"Countryside Hotel","description":"Save + up to 50% off traditional hotels. Free WiFi, great location near downtown, + full kitchen, washer & dryer, 24/7 support, bowling alley, fitness center + and more.","descriptionFr":"\u00c9conomisez jusqu''\u00e0 50% sur les h\u00f4tels + traditionnels. WiFi gratuit, tr\u00e8s bien situ\u00e9 pr\u00e8s du centre-ville, + cuisine compl\u00e8te, laveuse & s\u00e9cheuse, support 24/7, bowling, centre + de fitness et plus encore.","category":"Budget","tags":["24-hour front desk + service","coffee in lobby","restaurant"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1999-09-06T00:00:00Z","rating":3,"location":{"type":"Point","coordinates":[-78.940483,35.90416],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"6910 + Fayetteville Rd","city":"Durham","stateProvince":"NC","country":"USA","postalCode":"27713"},"rooms":[{"description":"Suite, + 1 King Bed (Amenities)","descriptionFr":"Suite, 1 tr\u00e8s grand lit (Services)","type":"Suite","baseRate":2.44,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["coffee maker"]},{"description":"Budget + Room, 1 Queen Bed (Amenities)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (Services)","type":"Budget Room","baseRate":7.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":false,"tags":["coffee maker"]}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '938' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:33 GMT + elapsed-time: '4' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 1dc3dc0c-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searche7410d98.search.windows.net/indexes('drgqefsg')/docs('10')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document_missing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document_missing.yaml new file mode 100644 index 000000000000..f078f745b3fd --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_document_missing.yaml @@ -0,0 +1,29 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 19B64B011F6E769F530EB9ABC70226A9 + method: GET + uri: https://search630210f1.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:23:44 GMT + elapsed-time: '85' + expires: '-1' + pragma: no-cache + request-id: 2452a5c6-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://search630210f1.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_filter.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_filter.yaml new file mode 100644 index 000000000000..f9e38d10cfbc --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_filter.yaml @@ -0,0 +1,45 @@ +interactions: +- request: + body: '{"filter": "category eq ''Budget''", "orderby": "hotelName desc", "search": + "WiFi", "select": "hotelName,category,description"}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '125' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 5526374BAE5FFA2D3753A74AB14F9988 + method: POST + uri: https://search30b50f94.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":0.19169211,"hotelName":"Express Rooms","description":"Pretty + good hotel","category":"Budget"},{"@search.score":0.19169211,"hotelName":"EconoStay","description":"Very + popular hotel in town","category":"Budget"},{"@search.score":0.2423066,"hotelName":"Countryside + Hotel","description":"Save up to 50% off traditional hotels. Free WiFi, great + location near downtown, full kitchen, washer & dryer, 24/7 support, bowling + alley, fitness center and more.","category":"Budget"},{"@search.score":0.19169211,"hotelName":"Comfy + Place","description":"Another good hotel","category":"Budget"}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '441' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:23:55 GMT + elapsed-time: '82' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 2a7c8886-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search30b50f94.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_simple.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_simple.yaml new file mode 100644 index 000000000000..07f2f8246bc6 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_get_search_simple.yaml @@ -0,0 +1,136 @@ +interactions: +- request: + body: '{"search": "hotel"}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '19' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 2F28B08BD827AF50E095AABB9A3234F0 + method: POST + uri: https://search30fc0f98.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":0.48248714,"hotelId":"10","hotelName":"Countryside + Hotel","description":"Save up to 50% off traditional hotels. Free WiFi, great + location near downtown, full kitchen, washer & dryer, 24/7 support, bowling + alley, fitness center and more.","descriptionFr":"\u00c9conomisez jusqu''\u00e0 + 50% sur les h\u00f4tels traditionnels. WiFi gratuit, tr\u00e8s bien situ\u00e9 + pr\u00e8s du centre-ville, cuisine compl\u00e8te, laveuse & s\u00e9cheuse, + support 24/7, bowling, centre de fitness et plus encore.","category":"Budget","tags":["24-hour + front desk service","coffee in lobby","restaurant"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1999-09-06T00:00:00Z","rating":3,"location":{"type":"Point","coordinates":[-78.940483,35.90416],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"6910 + Fayetteville Rd","city":"Durham","stateProvince":"NC","country":"USA","postalCode":"27713"},"rooms":[{"description":"Suite, + 1 King Bed (Amenities)","descriptionFr":"Suite, 1 tr\u00e8s grand lit (Services)","type":"Suite","baseRate":2.44,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["coffee maker"]},{"description":"Budget + Room, 1 Queen Bed (Amenities)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (Services)","type":"Budget Room","baseRate":7.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":false,"tags":["coffee maker"]}]},{"@search.score":0.18522348,"hotelId":"3","hotelName":"EconoStay","description":"Very + popular hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.18522348,"hotelId":"4","hotelName":"Express + Rooms","description":"Pretty good hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.18522348,"hotelId":"5","hotelName":"Comfy + Place","description":"Another good hotel","descriptionFr":"Un autre bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"2012-08-12T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.15049407,"hotelId":"2","hotelName":"Roach + Motel","description":"Cheapest hotel in town. Infact, a motel.","descriptionFr":"H\u00f4tel + le moins cher en ville. Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.12030617,"hotelId":"1","hotelName":"Fancy + Stay","description":"Best hotel in town if you like luxury hotels. They have + an amazing infinity pool, a spa, and a really helpful concierge. The location + is perfect -- right downtown, close to all the tourist attractions. We highly + recommend this hotel.","descriptionFr":"Meilleur h\u00f4tel en ville si vous + aimez les h\u00f4tels de luxe. Ils ont une magnifique piscine \u00e0 d\u00e9bordement, + un spa et un concierge tr\u00e8s utile. L''emplacement est parfait \u2013 + en plein centre, \u00e0 proximit\u00e9 de toutes les attractions touristiques. + Nous recommandons fortement cet h\u00f4tel.","category":"Luxury","tags":["pool","view","wifi","concierge"],"parkingIncluded":false,"smokingAllowed":false,"lastRenovationDate":"2010-06-27T00:00:00Z","rating":5,"location":{"type":"Point","coordinates":[-122.131577,47.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.057882335,"hotelId":"9","hotelName":"Secret + Point Motel","description":"The hotel is ideally located on the main commercial + artery of the city in the heart of New York. A few minutes away is Time''s + Square and the historic centre of the city, as well as other places of interest + that make New York one of America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '2377' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:05 GMT + elapsed-time: '106' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 30b655ce-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search30fc0f98.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview +- request: + body: '{"search": "motel"}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '19' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 2F28B08BD827AF50E095AABB9A3234F0 + method: POST + uri: https://search30fc0f98.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":1.2368374,"hotelId":"2","hotelName":"Roach + Motel","description":"Cheapest hotel in town. Infact, a motel.","descriptionFr":"H\u00f4tel + le moins cher en ville. Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.24176063,"hotelId":"9","hotelName":"Secret + Point Motel","description":"The hotel is ideally located on the main commercial + artery of the city in the heart of New York. A few minutes away is Time''s + Square and the historic centre of the city, as well as other places of interest + that make New York one of America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '1271' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:05 GMT + elapsed-time: '6' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 30d7a67a-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search30fc0f98.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_existing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_existing.yaml new file mode 100644 index 000000000000..2797ec396826 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_existing.yaml @@ -0,0 +1,137 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "3", "rating": 1, "@search.action": "merge"}, {"hotelId": + "4", "rating": 2, "@search.action": "merge"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '127' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 95C6A0FA23C5D84AC3889535541DDEA2 + method: POST + uri: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"3","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '190' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:17 GMT + elapsed-time: '109' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 376cee32-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 95C6A0FA23C5D84AC3889535541DDEA2 + method: GET + uri: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '127' + content-type: text/plain + date: Fri, 06 Mar 2020 22:24:20 GMT + elapsed-time: '23' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 395802e0-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 95C6A0FA23C5D84AC3889535541DDEA2 + method: GET + uri: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"3","hotelName":"EconoStay","description":"Very popular + hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '438' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:20 GMT + elapsed-time: '20' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 3961b556-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 95C6A0FA23C5D84AC3889535541DDEA2 + method: GET + uri: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '422' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:20 GMT + elapsed-time: '4' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 396bd25c-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchaaf712a5.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_missing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_missing.yaml new file mode 100644 index 000000000000..c421e870de91 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_documents_missing.yaml @@ -0,0 +1,132 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 1, "@search.action": "merge"}, + {"hotelId": "4", "rating": 2, "@search.action": "merge"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '130' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - F60FDCA77DC70D8F7949E76299676C86 + method: POST + uri: https://search98441234.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":false,"errorMessage":"Document not + found.","statusCode":404},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '225' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:31 GMT + elapsed-time: '94' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 3fed138e-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 207 + message: Multi-Status + url: https://search98441234.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - F60FDCA77DC70D8F7949E76299676C86 + method: GET + uri: https://search98441234.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '127' + content-type: text/plain + date: Fri, 06 Mar 2020 22:24:34 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 41d61e98-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search98441234.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - F60FDCA77DC70D8F7949E76299676C86 + method: GET + uri: https://search98441234.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: no-cache + content-length: '0' + date: Fri, 06 Mar 2020 22:24:34 GMT + elapsed-time: '3' + expires: '-1' + pragma: no-cache + request-id: 41dbc0d2-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found + url: https://search98441234.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - F60FDCA77DC70D8F7949E76299676C86 + method: GET + uri: https://search98441234.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '422' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:34 GMT + elapsed-time: '8' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 41e0a804-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search98441234.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_or_upload_documents.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_or_upload_documents.yaml new file mode 100644 index 000000000000..580cd247328e --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_merge_or_upload_documents.yaml @@ -0,0 +1,136 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 1, "@search.action": "mergeOrUpload"}, + {"hotelId": "4", "rating": 2, "@search.action": "mergeOrUpload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '146' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852A8ABCE142584DEBB9356D4BACB2C + method: POST + uri: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '196' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:45 GMT + elapsed-time: '30' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 4845fed8-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852A8ABCE142584DEBB9356D4BACB2C + method: GET + uri: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF11" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '127' + content-type: text/plain + date: Fri, 06 Mar 2020 22:24:48 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 4a2888ba-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852A8ABCE142584DEBB9356D4BACB2C + method: GET + uri: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1000","hotelName":null,"description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":1,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '257' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:48 GMT + elapsed-time: '8' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 4a2dfd54-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0852A8ABCE142584DEBB9356D4BACB2C + method: GET + uri: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '422' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:48 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 4a33d5e4-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbd5d12ff.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_suggest.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_suggest.yaml new file mode 100644 index 000000000000..9805cad40c10 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_suggest.yaml @@ -0,0 +1,39 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 0FF7E99CF5014B03D89C7C4F84F39D91 + method: POST + uri: https://searcha8490b9c.search.windows.net/indexes('drgqefsg')/docs/search.post.suggest?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.text":"Cheapest hotel in town. Infact, a motel.","hotelId":"2"},{"@search.text":"Secret + Point Motel","hotelId":"9"}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '216' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:24:59 GMT + elapsed-time: '123' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 507362c6-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searcha8490b9c.search.windows.net/indexes('drgqefsg')/docs/search.post.suggest?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_existing.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_existing.yaml new file mode 100644 index 000000000000..32881a95675a --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_existing.yaml @@ -0,0 +1,40 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure + Inn", "@search.action": "upload"}, {"hotelId": "3", "rating": 4, "rooms": [], + "hotelName": "Redmond Hotel", "@search.action": "upload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '214' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 86794A0BE338AD5C6E40AAEF0077C668 + method: POST + uri: https://searchbf13131a.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"3","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '196' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:25:09 GMT + elapsed-time: '83' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 56e436c6-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://searchbf13131a.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_new.yaml b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_new.yaml new file mode 100644 index 000000000000..70b59d8cecf4 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/recordings/test_live_async.test_upload_documents_new.yaml @@ -0,0 +1,136 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure + Inn", "@search.action": "upload"}, {"hotelId": "1001", "rating": 4, "rooms": + [], "hotelName": "Redmond Hotel", "@search.action": "upload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Content-Length: + - '217' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 41E2142A9A2513C75C8776A73B89CA91 + method: POST + uri: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"1001","status":true,"errorMessage":null,"statusCode":201}]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '193' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:25:19 GMT + elapsed-time: '101' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 5d0624f6-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 41E2142A9A2513C75C8776A73B89CA91 + method: GET + uri: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF12" + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '127' + content-type: text/plain + date: Fri, 06 Mar 2020 22:25:23 GMT + elapsed-time: '5' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 5ef042e2-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 41E2142A9A2513C75C8776A73B89CA91 + method: GET + uri: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1000","hotelName":"Azure Inn","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":5,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '267' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:25:23 GMT + elapsed-time: '8' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 5ef5b524-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 41E2142A9A2513C75C8776A73B89CA91 + method: GET + uri: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs('1001')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1001","hotelName":"Redmond Hotel","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":4,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: no-cache + content-encoding: gzip + content-length: '268' + content-type: application/json; odata.metadata=none + date: Fri, 06 Mar 2020 22:25:23 GMT + elapsed-time: '4' + expires: '-1' + odata-version: '4.0' + pragma: no-cache + preference-applied: odata.include-annotations="*" + request-id: 5efb9f98-5ff9-11ea-b13a-8c8590507855 + strict-transport-security: max-age=15724800; includeSubDomains + vary: Accept-Encoding + status: + code: 200 + message: OK + url: https://search63c010f9.search.windows.net/indexes('drgqefsg')/docs('1001')?api-version=2019-05-06-Preview +version: 1 diff --git a/sdk/search/azure-search/tests/async_tests/test_live_async.py b/sdk/search/azure-search/tests/async_tests/test_live_async.py new file mode 100644 index 000000000000..340ac0d8ef24 --- /dev/null +++ b/sdk/search/azure-search/tests/async_tests/test_live_async.py @@ -0,0 +1,321 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import asyncio +import functools +import json +from os.path import dirname, join, realpath +import time + +import pytest + +from devtools_testutils import AzureMgmtTestCase, ResourceGroupPreparer + +from search_service_preparer import SearchServicePreparer + +from azure_devtools.scenario_tests.utilities import trim_kwargs_from_test_function + +CWD = dirname(realpath(__file__)) + +SCHEMA = open(join(CWD, "..", "hotel_schema.json")).read() +BATCH = json.load(open(join(CWD, "..", "hotel_small.json"))) + +from azure.core.exceptions import HttpResponseError +from azure.search import AutocompleteQuery, SearchApiKeyCredential, SearchQuery, SuggestQuery +from azure.search.aio import SearchIndexClient + + +def await_prepared_test(test_fn): + """Synchronous wrapper for async test methods. Used to avoid making changes + upstream to AbstractPreparer (which doesn't await the functions it wraps) + """ + + @functools.wraps(test_fn) + def run(test_class_instance, *args, **kwargs): + trim_kwargs_from_test_function(test_fn, kwargs) + loop = asyncio.get_event_loop() + return loop.run_until_complete(test_fn(test_class_instance, **kwargs)) + + return run + +class SearchIndexClientTestAsync(AzureMgmtTestCase): + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_async_get_document_count(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + assert await client.get_document_count() == 10 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_get_document(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + for hotel_id in range(1, 11): + result = await client.get_document(key=str(hotel_id)) + expected = BATCH['value'][hotel_id-1] + assert result.get("hotelId") == expected.get("hotelId") + assert result.get("hotelName") == expected.get("hotelName") + assert result.get("description") == expected.get("description") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_get_document_missing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + with pytest.raises(HttpResponseError): + await client.get_document(key="1000") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_get_search_simple(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = [] + async for x in await client.search(query="hotel"): + results.append(x) + assert len(results) == 7 + + results = [] + async for x in await client.search(query="motel"): + results.append(x) + assert len(results) == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_get_search_filter(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + + query = SearchQuery(search_text="WiFi") + query.filter("category eq 'Budget'") + query.select("hotelName", "category", "description") + query.order_by("hotelName desc") + + async with client: + results = [] + async for x in await client.search(query=query): + results.append(x) + assert [x['hotelName'] for x in results] == sorted([x['hotelName'] for x in results], reverse=True) + expected = {"category", "hotelName", "description", "@search.score", "@search.highlights"} + assert all(set(x) == expected for x in results) + assert all(x['category'] == "Budget" for x in results) + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_autocomplete(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + query = AutocompleteQuery(search_text="mot", suggester_name="sg") + results = await client.autocomplete(query=query) + assert results == [{'text': 'motel', 'query_plus_text': 'motel'}] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_suggest(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + query = SuggestQuery(search_text="mot", suggester_name="sg") + results = await client.suggest(query=query) + assert results == [ + {'hotelId': '2', 'text': 'Cheapest hotel in town. Infact, a motel.'}, + {'hotelId': '9', 'text': 'Secret Point Motel'}, + ] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_upload_documents_new(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + DOCUMENTS = [{ + 'hotelId': '1000', + 'rating': 5, + 'rooms': [], + 'hotelName': 'Azure Inn', + }, + { + 'hotelId': '1001', + 'rating': 4, + 'rooms': [], + 'hotelName': 'Redmond Hotel', + }] + + async with client: + results = await client.upload_documents(DOCUMENTS) + assert len(results) == 2 + assert set(x.status_code for x in results) == {201} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 12 + for doc in DOCUMENTS: + result = await client.get_document(key=doc['hotelId']) + assert result['hotelId'] == doc['hotelId'] + assert result['hotelName'] == doc['hotelName'] + assert result['rating'] == doc['rating'] + assert result['rooms'] == doc['rooms'] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_upload_documents_existing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + DOCUMENTS = [{ + 'hotelId': '1000', + 'rating': 5, + 'rooms': [], + 'hotelName': 'Azure Inn', + }, + { + 'hotelId': '3', + 'rating': 4, + 'rooms': [], + 'hotelName': 'Redmond Hotel', + }] + async with client: + results = await client.upload_documents(DOCUMENTS) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 201} + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_delete_documents_existing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = await client.delete_documents([{"hotelId": "3"}, {"hotelId": "4"}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 8 + + with pytest.raises(HttpResponseError): + await client.get_document(key="3") + + with pytest.raises(HttpResponseError): + await client.get_document(key="4") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_delete_documents_missing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = await client.delete_documents([{"hotelId": "1000"}, {"hotelId": "4"}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 9 + + with pytest.raises(HttpResponseError): + await client.get_document(key="1000") + + with pytest.raises(HttpResponseError): + await client.get_document(key="4") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_merge_documents_existing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = await client.merge_documents([{"hotelId": "3", "rating": 1}, {"hotelId": "4", "rating": 2}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 10 + + result = await client.get_document(key="3") + assert result["rating"] == 1 + + result = await client.get_document(key="4") + assert result["rating"] == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_merge_documents_missing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = await client.merge_documents([{"hotelId": "1000", "rating": 1}, {"hotelId": "4", "rating": 2}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 404} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 10 + + with pytest.raises(HttpResponseError): + await client.get_document(key="1000") + + result = await client.get_document(key="4") + assert result["rating"] == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + @await_prepared_test + async def test_merge_or_upload_documents(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + async with client: + results = await client.merge_or_upload_documents([{"hotelId": "1000", "rating": 1}, {"hotelId": "4", "rating": 2}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 201} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert await client.get_document_count() == 11 + + result = await client.get_document(key="1000") + assert result["rating"] == 1 + + result = await client.get_document(key="4") + assert result["rating"] == 2 diff --git a/sdk/search/azure-search/tests/conftest.py b/sdk/search/azure-search/tests/conftest.py new file mode 100644 index 000000000000..d129d1baf24a --- /dev/null +++ b/sdk/search/azure-search/tests/conftest.py @@ -0,0 +1,14 @@ +# ------------------------------------------------------------------------ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ------------------------------------------------------------------------- + +import sys + +import pytest + +# Ignore async tests for Python < 3.5 +collect_ignore = [] +if sys.version_info < (3, 5): + collect_ignore.append("async_tests") diff --git a/sdk/search/azure-search/tests/consts.py b/sdk/search/azure-search/tests/consts.py new file mode 100644 index 000000000000..2aad214b3224 --- /dev/null +++ b/sdk/search/azure-search/tests/consts.py @@ -0,0 +1,9 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ----------------------------------- + +TEST_SERVICE_NAME = "test-service-name" +SERVICE_URL = "https://{}.search.windows.net/indexes?api-version=2019-05-06".format( + TEST_SERVICE_NAME +) diff --git a/sdk/search/azure-search/tests/hotel_schema.json b/sdk/search/azure-search/tests/hotel_schema.json new file mode 100644 index 000000000000..606379345d48 --- /dev/null +++ b/sdk/search/azure-search/tests/hotel_schema.json @@ -0,0 +1,288 @@ +{ + "name": "drgqefsg", + "fields": [ + { + "name": "hotelId", + "type": "Edm.String", + "key": true, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "hotelName", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": false + }, + { + "name": "description", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "en.lucene" + }, + { + "name": "descriptionFr", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "fr.lucene" + }, + { + "name": "category", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "tags", + "type": "Collection(Edm.String)", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "parkingIncluded", + "type": "Edm.Boolean", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "smokingAllowed", + "type": "Edm.Boolean", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "lastRenovationDate", + "type": "Edm.DateTimeOffset", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "rating", + "type": "Edm.Int32", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "location", + "type": "Edm.GeographyPoint", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": true, + "facetable": false + }, + { + "name": "address", + "type": "Edm.ComplexType", + "fields": [ + { + "name": "streetAddress", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false + }, + { + "name": "city", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "stateProvince", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "country", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": true + }, + { + "name": "postalCode", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": true, + "facetable": true + } + ] + }, + { + "name": "rooms", + "type": "Collection(Edm.ComplexType)", + "fields": [ + { + "name": "description", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "en.lucene" + }, + { + "name": "descriptionFr", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": false, + "sortable": false, + "facetable": false, + "analyzer": "fr.lucene" + }, + { + "name": "type", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "baseRate", + "type": "Edm.Double", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "bedOptions", + "type": "Edm.String", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "sleepsCount", + "type": "Edm.Int32", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "smokingAllowed", + "type": "Edm.Boolean", + "key": false, + "retrievable": true, + "searchable": false, + "filterable": true, + "sortable": false, + "facetable": true + }, + { + "name": "tags", + "type": "Collection(Edm.String)", + "key": false, + "retrievable": true, + "searchable": true, + "filterable": true, + "sortable": false, + "facetable": true + } + ] + } + ], + "scoringProfiles": [ + { + "name": "nearest", + "functions": [ + { + "type": "distance", + "distance": { + "referencePointParameter": "myloc", + "boostingDistance": 100.0 + }, + "fieldName": "location", + "boost": 2.0 + } + ], + "functionAggregation": "sum" + } + ], + "suggesters": [ + { + "name": "sg", + "sourceFields": [ + "description", + "hotelName" + ], + "searchMode": "analyzingInfixMatching" + } + ] +} \ No newline at end of file diff --git a/sdk/search/azure-search/tests/hotel_small.json b/sdk/search/azure-search/tests/hotel_small.json new file mode 100644 index 000000000000..6ea1027a61e3 --- /dev/null +++ b/sdk/search/azure-search/tests/hotel_small.json @@ -0,0 +1,254 @@ +{ + "value": [ + { + "@search.action": "upload", + "hotelId": "1", + "hotelName": "Fancy Stay", + "description": "Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa, and a really helpful concierge. The location is perfect -- right downtown, close to all the tourist attractions. We highly recommend this hotel.", + "descriptionFr": "Meilleur hôtel en ville si vous aimez les hôtels de luxe. Ils ont une magnifique piscine à débordement, un spa et un concierge très utile. L'emplacement est parfait – en plein centre, à proximité de toutes les attractions touristiques. Nous recommandons fortement cet hôtel.", + "category": "Luxury", + "tags": [ + "pool", + "view", + "wifi", + "concierge" + ], + "parkingIncluded": false, + "smokingAllowed": false, + "lastRenovationDate": "2010-06-27T00:00:00+00:00", + "rating": 5, + "location": { + "type": "Point", + "coordinates": [ + -122.131577, + 47.678581 + ] + } + }, + { + "@search.action": "upload", + "hotelId": "2", + "hotelName": "Roach Motel", + "description": "Cheapest hotel in town. Infact, a motel.", + "descriptionFr": "Hôtel le moins cher en ville. Infact, un motel.", + "category": "Budget", + "tags": [ + "motel", + "budget" + ], + "parkingIncluded": true, + "smokingAllowed": true, + "lastRenovationDate": "1982-04-28T00:00:00+00:00", + "rating": 1, + "location": { + "type": "Point", + "coordinates": [ + -122.131577, + 49.678581 + ] + } + }, + { + "@search.action": "upload", + "hotelId": "3", + "hotelName": "EconoStay", + "description": "Very popular hotel in town", + "descriptionFr": "Hôtel le plus populaire en ville", + "category": "Budget", + "tags": [ + "wifi", + "budget" + ], + "parkingIncluded": true, + "smokingAllowed": false, + "lastRenovationDate": "1995-07-01T00:00:00+00:00", + "rating": 4, + "location": { + "type": "Point", + "coordinates": [ + -122.131577, + 46.678581 + ] + } + }, + { + "@search.action": "upload", + "hotelId": "4", + "hotelName": "Express Rooms", + "description": "Pretty good hotel", + "descriptionFr": "Assez bon hôtel", + "category": "Budget", + "tags": [ + "wifi", + "budget" + ], + "parkingIncluded": true, + "smokingAllowed": false, + "lastRenovationDate": "1995-07-01T00:00:00+00:00", + "rating": 4, + "location": { + "type": "Point", + "coordinates": [ + -122.131577, + 48.678581 + ] + } + }, + { + "@search.action": "upload", + "hotelId": "5", + "hotelName": "Comfy Place", + "description": "Another good hotel", + "descriptionFr": "Un autre bon hôtel", + "category": "Budget", + "tags": [ + "wifi", + "budget" + ], + "parkingIncluded": true, + "smokingAllowed": false, + "lastRenovationDate": "2012-08-12T00:00:00+00:00", + "rating": 4, + "location": { + "type": "Point", + "coordinates": [ + -122.131577, + 48.678581 + ] + } + }, + { + "@search.action": "upload", + "hotelId": "6", + "description": "Surprisingly expensive. Model suites have an ocean-view." + }, + { + "@search.action": "upload", + "hotelId": "7", + "hotelName": "Modern Stay", + "description": "Modern architecture, very polite staff and very clean. Also very affordable.", + "descriptionFr": "Architecture moderne, personnel poli et très propre. Aussi très abordable." + }, + { + "@search.action": "upload", + "hotelId": "8", + "description": "Has some road noise and is next to the very police station. Bathrooms had morel coverings.", + "descriptionFr": "Il y a du bruit de la route et se trouve à côté de la station de police. Les salles de bain avaient des revêtements de morilles." + }, + { + "@search.action": "upload", + "hotelId": "9", + "hotelName": "Secret Point Motel", + "description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.", + "descriptionFr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.", + "category": "Boutique", + "tags": [ + "pool", + "air conditioning", + "concierge" + ], + "parkingIncluded": false, + "smokingAllowed": true, + "lastRenovationDate": "1970-01-18T00:00:00-05:00", + "rating": 4, + "location": { + "type": "Point", + "coordinates": [ + -73.975403, + 40.760586 + ] + }, + "address": { + "streetAddress": "677 5th Ave", + "city": "New York", + "stateProvince": "NY", + "country": "USA", + "postalCode": "10022" + }, + "rooms": [ + { + "description": "Budget Room, 1 Queen Bed (Cityside)", + "descriptionFr": "Chambre Économique, 1 grand lit (côté ville)", + "type": "Budget Room", + "baseRate": 9.69, + "bedOptions": "1 Queen Bed", + "sleepsCount": 2, + "smokingAllowed": true, + "tags": [ + "vcr/dvd" + ] + }, + { + "description": "Budget Room, 1 King Bed (Mountain View)", + "descriptionFr": "Chambre Économique, 1 très grand lit (Mountain View)", + "type": "Budget Room", + "baseRate": 8.09, + "bedOptions": "1 King Bed", + "sleepsCount": 2, + "smokingAllowed": true, + "tags": [ + "vcr/dvd", + "jacuzzi tub" + ] + } + ] + }, + { + "@search.action": "upload", + "hotelId": "10", + "hotelName": "Countryside Hotel", + "description": "Save up to 50% off traditional hotels. Free WiFi, great location near downtown, full kitchen, washer & dryer, 24/7 support, bowling alley, fitness center and more.", + "descriptionFr": "Économisez jusqu'à 50% sur les hôtels traditionnels. WiFi gratuit, très bien situé près du centre-ville, cuisine complète, laveuse & sécheuse, support 24/7, bowling, centre de fitness et plus encore.", + "category": "Budget", + "tags": [ + "24-hour front desk service", + "coffee in lobby", + "restaurant" + ], + "parkingIncluded": false, + "smokingAllowed": true, + "lastRenovationDate": "1999-09-06T00:00:00+00:00", + "rating": 3, + "location": { + "type": "Point", + "coordinates": [ + -78.940483, + 35.90416 + ] + }, + "address": { + "streetAddress": "6910 Fayetteville Rd", + "city": "Durham", + "stateProvince": "NC", + "country": "USA", + "postalCode": "27713" + }, + "rooms": [ + { + "description": "Suite, 1 King Bed (Amenities)", + "descriptionFr": "Suite, 1 très grand lit (Services)", + "type": "Suite", + "baseRate": 2.44, + "bedOptions": "1 King Bed", + "sleepsCount": 2, + "smokingAllowed": true, + "tags": [ + "coffee maker" + ] + }, + { + "description": "Budget Room, 1 Queen Bed (Amenities)", + "descriptionFr": "Chambre Économique, 1 grand lit (Services)", + "type": "Budget Room", + "baseRate": 7.69, + "bedOptions": "1 Queen Bed", + "sleepsCount": 2, + "smokingAllowed": false, + "tags": [ + "coffee maker" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_autocomplete.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_autocomplete.yaml new file mode 100644 index 000000000000..66f490dc0c24 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_autocomplete.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 98D70580239DCF923C06ACEC981D4CF1 + method: POST + uri: https://search9c0e0b2f.search.windows.net/indexes('drgqefsg')/docs/search.post.autocomplete?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"text":"motel","queryPlusText":"motel"}]}' + headers: + cache-control: + - no-cache + content-length: + - '52' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:19:49 GMT + elapsed-time: + - '169' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - 97a1af3c-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_basic.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_basic.yaml new file mode 100644 index 000000000000..f9786dfca4f1 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_basic.yaml @@ -0,0 +1,48 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - CD6D4088F021D9EDF7DFB3DBD9BF207D + method: GET + uri: https://test-service-name.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06 + response: + body: + string: "\uFEFF10" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Thu, 05 Mar 2020 17:48:42 GMT + elapsed-time: + - '63' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - 8d76ef08-5f09-11ea-b10a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_existing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_existing.yaml new file mode 100644 index 000000000000..c79851d228c1 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_existing.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "3", "@search.action": "delete"}, {"hotelId": "4", + "@search.action": "delete"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '103' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - C0FE0A789A5790D14886269E3F30B932 + method: POST + uri: https://search5091108b.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"3","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '137' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:19:59 GMT + elapsed-time: + - '76' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - 9e1a5d1e-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - C0FE0A789A5790D14886269E3F30B932 + method: GET + uri: https://search5091108b.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF8" + headers: + cache-control: + - no-cache + content-length: + - '4' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:20:03 GMT + elapsed-time: + - '23' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - a009364a-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - C0FE0A789A5790D14886269E3F30B932 + method: GET + uri: https://search5091108b.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:20:03 GMT + elapsed-time: + - '19' + expires: + - '-1' + pragma: + - no-cache + request-id: + - a015d0c6-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - C0FE0A789A5790D14886269E3F30B932 + method: GET + uri: https://search5091108b.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:20:03 GMT + elapsed-time: + - '5' + expires: + - '-1' + pragma: + - no-cache + request-id: + - a0248ada-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_missing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_missing.yaml new file mode 100644 index 000000000000..1e1108f2373f --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_delete_documents_missing.yaml @@ -0,0 +1,175 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "@search.action": "delete"}, {"hotelId": + "4", "@search.action": "delete"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '106' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 734F52F602E2DE45AC2BFBC92A63DCB0 + method: POST + uri: https://search3ff8101a.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '140' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:14 GMT + elapsed-time: + - '101' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - a6b5bfc2-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 734F52F602E2DE45AC2BFBC92A63DCB0 + method: GET + uri: https://search3ff8101a.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF9" + headers: + cache-control: + - no-cache + content-length: + - '4' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:20:17 GMT + elapsed-time: + - '23' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - a8ab184a-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 734F52F602E2DE45AC2BFBC92A63DCB0 + method: GET + uri: https://search3ff8101a.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:20:17 GMT + elapsed-time: + - '20' + expires: + - '-1' + pragma: + - no-cache + request-id: + - a8b7c6b2-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 734F52F602E2DE45AC2BFBC92A63DCB0 + method: GET + uri: https://search3ff8101a.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:20:17 GMT + elapsed-time: + - '4' + expires: + - '-1' + pragma: + - no-cache + request-id: + - a8c1cb3a-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete.yaml new file mode 100644 index 000000000000..3d46c0d2a592 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 6CF152470BA4BF2B1A1ED6A61D8F5945 + method: POST + uri: https://searchcc080cce.search.windows.net/indexes('drgqefsg')/docs/search.post.autocomplete?api-version=2019-05-06 + response: + body: + string: '{"value":[{"text":"motel","queryPlusText":"motel"}]}' + headers: + cache-control: + - no-cache + content-length: + - '52' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 20:22:33 GMT + elapsed-time: + - '16' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - 0bdb9fdc-5f1f-11ea-8316-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete_simple.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete_simple.yaml new file mode 100644 index 000000000000..48831785afb3 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_autocomplete_simple.yaml @@ -0,0 +1,52 @@ +interactions: +- request: + body: '{"search": "Bud", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 2B86FA083D1F82F305D6A73D60077FDA + method: POST + uri: https://search31520fb7.search.windows.net/indexes('drgqefsg')/docs/search.post.autocomplete?api-version=2019-05-06 + response: + body: + string: '{"value":[]}' + headers: + cache-control: + - no-cache + content-length: + - '12' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 20:20:24 GMT + elapsed-time: + - '143' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - bed40fc6-5f1e-11ea-a115-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_document.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_document.yaml new file mode 100644 index 000000000000..4606e1275202 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_document.yaml @@ -0,0 +1,510 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('1')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1","hotelName":"Fancy Stay","description":"Best hotel in + town if you like luxury hotels. They have an amazing infinity pool, a spa, + and a really helpful concierge. The location is perfect -- right downtown, + close to all the tourist attractions. We highly recommend this hotel.","descriptionFr":"Meilleur + h\u00f4tel en ville si vous aimez les h\u00f4tels de luxe. Ils ont une magnifique + piscine \u00e0 d\u00e9bordement, un spa et un concierge tr\u00e8s utile. L''emplacement + est parfait \u2013 en plein centre, \u00e0 proximit\u00e9 de toutes les attractions + touristiques. Nous recommandons fortement cet h\u00f4tel.","category":"Luxury","tags":["pool","view","wifi","concierge"],"parkingIncluded":false,"smokingAllowed":false,"lastRenovationDate":"2010-06-27T00:00:00Z","rating":5,"location":{"type":"Point","coordinates":[-122.131577,47.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '940' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '103' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - aee35718-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('2')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"2","hotelName":"Roach Motel","description":"Cheapest hotel + in town. Infact, a motel.","descriptionFr":"H\u00f4tel le moins cher en ville. + Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '463' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '4' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af113b38-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"3","hotelName":"EconoStay","description":"Very popular + hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '432' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af193dd8-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '410' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '6' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af213eca-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('5')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"5","hotelName":"Comfy Place","description":"Another good + hotel","descriptionFr":"Un autre bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"2012-08-12T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '412' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af2956be-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('6')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"6","hotelName":null,"description":"Surprisingly expensive. + Model suites have an ocean-view.","descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '279' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '7' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af31038c-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('7')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"7","hotelName":"Modern Stay","description":"Modern architecture, + very polite staff and very clean. Also very affordable.","descriptionFr":"Architecture + moderne, personnel poli et tr\u00e8s propre. Aussi tr\u00e8s abordable.","category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '390' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af3947d6-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('8')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"8","hotelName":null,"description":"Has some road noise + and is next to the very police station. Bathrooms had morel coverings.","descriptionFr":"Il + y a du bruit de la route et se trouve \u00e0 c\u00f4t\u00e9 de la station + de police. Les salles de bain avaient des rev\u00eatements de morilles.","category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":null,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '459' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '4' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af411bb4-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('9')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"9","hotelName":"Secret Point Motel","description":"The + hotel is ideally located on the main commercial artery of the city in the + heart of New York. A few minutes away is Time''s Square and the historic centre + of the city, as well as other places of interest that make New York one of + America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}' + headers: + cache-control: + - no-cache + content-length: + - '1664' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '12' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af48d098-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 951DBF79159AA461453D7CC1A63966B8 + method: GET + uri: https://search9b020b1b.search.windows.net/indexes('drgqefsg')/docs('10')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"10","hotelName":"Countryside Hotel","description":"Save + up to 50% off traditional hotels. Free WiFi, great location near downtown, + full kitchen, washer & dryer, 24/7 support, bowling alley, fitness center + and more.","descriptionFr":"\u00c9conomisez jusqu''\u00e0 50% sur les h\u00f4tels + traditionnels. WiFi gratuit, tr\u00e8s bien situ\u00e9 pr\u00e8s du centre-ville, + cuisine compl\u00e8te, laveuse & s\u00e9cheuse, support 24/7, bowling, centre + de fitness et plus encore.","category":"Budget","tags":["24-hour front desk + service","coffee in lobby","restaurant"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1999-09-06T00:00:00Z","rating":3,"location":{"type":"Point","coordinates":[-78.940483,35.90416],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"6910 + Fayetteville Rd","city":"Durham","stateProvince":"NC","country":"USA","postalCode":"27713"},"rooms":[{"description":"Suite, + 1 King Bed (Amenities)","descriptionFr":"Suite, 1 tr\u00e8s grand lit (Services)","type":"Suite","baseRate":2.44,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["coffee maker"]},{"description":"Budget + Room, 1 Queen Bed (Amenities)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (Services)","type":"Budget Room","baseRate":7.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":false,"tags":["coffee maker"]}]}' + headers: + cache-control: + - no-cache + content-length: + - '1406' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:20:28 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af51c0f4-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_document_count.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_document_count.yaml new file mode 100644 index 000000000000..e2111c389be3 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_document_count.yaml @@ -0,0 +1,48 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A0809559B1982EED5D4D2046192E3C9F + method: GET + uri: https://searche6380da3.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:20:40 GMT + elapsed-time: + - '53' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - b616a4a4-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_document_missing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_document_missing.yaml new file mode 100644 index 000000000000..9116e775a3f6 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_document_missing.yaml @@ -0,0 +1,40 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - DD3FF4F33AECD9C561E4E6898CE4D05B + method: GET + uri: https://search2db0e74.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:20:50 GMT + elapsed-time: + - '68' + expires: + - '-1' + pragma: + - no-cache + request-id: + - bc218b20-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_search_filter.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_search_filter.yaml new file mode 100644 index 000000000000..08f8b2a390dc --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_search_filter.yaml @@ -0,0 +1,59 @@ +interactions: +- request: + body: '{"filter": "category eq ''Budget''", "orderby": "hotelName desc", "search": + "WiFi", "select": "hotelName,category,description"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '125' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 3F20D06B106E429D1E32BC1813E3D826 + method: POST + uri: https://searchd7f60d17.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":0.19169211,"hotelName":"Express Rooms","description":"Pretty + good hotel","category":"Budget"},{"@search.score":0.19169211,"hotelName":"EconoStay","description":"Very + popular hotel in town","category":"Budget"},{"@search.score":0.2423066,"hotelName":"Countryside + Hotel","description":"Save up to 50% off traditional hotels. Free WiFi, great + location near downtown, full kitchen, washer & dryer, 24/7 support, bowling + alley, fitness center and more.","category":"Budget"},{"@search.score":0.19169211,"hotelName":"Comfy + Place","description":"Another good hotel","category":"Budget"}]}' + headers: + cache-control: + - no-cache + content-length: + - '609' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:01 GMT + elapsed-time: + - '125' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - c26afde0-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_search_simple.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_search_simple.yaml new file mode 100644 index 000000000000..6e1bad01f0be --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_search_simple.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: '{"search": "hotel"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '19' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A8B6B37DA02274FB63AA22FEFA71E558 + method: POST + uri: https://searchd83d0d1b.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":0.48248714,"hotelId":"10","hotelName":"Countryside + Hotel","description":"Save up to 50% off traditional hotels. Free WiFi, great + location near downtown, full kitchen, washer & dryer, 24/7 support, bowling + alley, fitness center and more.","descriptionFr":"\u00c9conomisez jusqu''\u00e0 + 50% sur les h\u00f4tels traditionnels. WiFi gratuit, tr\u00e8s bien situ\u00e9 + pr\u00e8s du centre-ville, cuisine compl\u00e8te, laveuse & s\u00e9cheuse, + support 24/7, bowling, centre de fitness et plus encore.","category":"Budget","tags":["24-hour + front desk service","coffee in lobby","restaurant"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1999-09-06T00:00:00Z","rating":3,"location":{"type":"Point","coordinates":[-78.940483,35.90416],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"6910 + Fayetteville Rd","city":"Durham","stateProvince":"NC","country":"USA","postalCode":"27713"},"rooms":[{"description":"Suite, + 1 King Bed (Amenities)","descriptionFr":"Suite, 1 tr\u00e8s grand lit (Services)","type":"Suite","baseRate":2.44,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["coffee maker"]},{"description":"Budget + Room, 1 Queen Bed (Amenities)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (Services)","type":"Budget Room","baseRate":7.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":false,"tags":["coffee maker"]}]},{"@search.score":0.18522348,"hotelId":"3","hotelName":"EconoStay","description":"Very + popular hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.18522348,"hotelId":"4","hotelName":"Express + Rooms","description":"Pretty good hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.18522348,"hotelId":"5","hotelName":"Comfy + Place","description":"Another good hotel","descriptionFr":"Un autre bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"2012-08-12T00:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.15049407,"hotelId":"2","hotelName":"Roach + Motel","description":"Cheapest hotel in town. Infact, a motel.","descriptionFr":"H\u00f4tel + le moins cher en ville. Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.12030617,"hotelId":"1","hotelName":"Fancy + Stay","description":"Best hotel in town if you like luxury hotels. They have + an amazing infinity pool, a spa, and a really helpful concierge. The location + is perfect -- right downtown, close to all the tourist attractions. We highly + recommend this hotel.","descriptionFr":"Meilleur h\u00f4tel en ville si vous + aimez les h\u00f4tels de luxe. Ils ont une magnifique piscine \u00e0 d\u00e9bordement, + un spa et un concierge tr\u00e8s utile. L''emplacement est parfait \u2013 + en plein centre, \u00e0 proximit\u00e9 de toutes les attractions touristiques. + Nous recommandons fortement cet h\u00f4tel.","category":"Luxury","tags":["pool","view","wifi","concierge"],"parkingIncluded":false,"smokingAllowed":false,"lastRenovationDate":"2010-06-27T00:00:00Z","rating":5,"location":{"type":"Point","coordinates":[-122.131577,47.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.057882335,"hotelId":"9","hotelName":"Secret + Point Motel","description":"The hotel is ideally located on the main commercial + artery of the city in the heart of New York. A few minutes away is Time''s + Square and the historic centre of the city, as well as other places of interest + that make New York one of America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}]}' + headers: + cache-control: + - no-cache + content-length: + - '5935' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:12 GMT + elapsed-time: + - '155' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - c92e19be-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: '{"search": "motel"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '19' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - A8B6B37DA02274FB63AA22FEFA71E558 + method: POST + uri: https://searchd83d0d1b.search.windows.net/indexes('drgqefsg')/docs/search.post.search?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.score":1.2368374,"hotelId":"2","hotelName":"Roach + Motel","description":"Cheapest hotel in town. Infact, a motel.","descriptionFr":"H\u00f4tel + le moins cher en ville. Infact, un motel.","category":"Budget","tags":["motel","budget"],"parkingIncluded":true,"smokingAllowed":true,"lastRenovationDate":"1982-04-28T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,49.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]},{"@search.score":0.24176063,"hotelId":"9","hotelName":"Secret + Point Motel","description":"The hotel is ideally located on the main commercial + artery of the city in the heart of New York. A few minutes away is Time''s + Square and the historic centre of the city, as well as other places of interest + that make New York one of America''s most attractive and cosmopolitan cities.","descriptionFr":"L''h\u00f4tel + est id\u00e9alement situ\u00e9 sur la principale art\u00e8re commerciale de + la ville en plein c\u0153ur de New York. A quelques minutes se trouve la place + du temps et le centre historique de la ville, ainsi que d''autres lieux d''int\u00e9r\u00eat + qui font de New York l''une des villes les plus attractives et cosmopolites + de l''Am\u00e9rique.","category":"Boutique","tags":["pool","air conditioning","concierge"],"parkingIncluded":false,"smokingAllowed":true,"lastRenovationDate":"1970-01-18T05:00:00Z","rating":4,"location":{"type":"Point","coordinates":[-73.975403,40.760586],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":{"streetAddress":"677 + 5th Ave","city":"New York","stateProvince":"NY","country":"USA","postalCode":"10022"},"rooms":[{"description":"Budget + Room, 1 Queen Bed (Cityside)","descriptionFr":"Chambre \u00c9conomique, 1 + grand lit (c\u00f4t\u00e9 ville)","type":"Budget Room","baseRate":9.69,"bedOptions":"1 + Queen Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd"]},{"description":"Budget + Room, 1 King Bed (Mountain View)","descriptionFr":"Chambre \u00c9conomique, + 1 tr\u00e8s grand lit (Mountain View)","type":"Budget Room","baseRate":8.09,"bedOptions":"1 + King Bed","sleepsCount":2,"smokingAllowed":true,"tags":["vcr/dvd","jacuzzi + tub"]}]}]}' + headers: + cache-control: + - no-cache + content-length: + - '2193' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:12 GMT + elapsed-time: + - '7' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - c960ba0e-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_get_suggest.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_get_suggest.yaml new file mode 100644 index 000000000000..83d80c039cdd --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_get_suggest.yaml @@ -0,0 +1,53 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 0CBB4FBBC5AC9CC7D2E5ED9B5299845D + method: POST + uri: https://search905a0abe.search.windows.net/indexes('drgqefsg')/docs/search.post.suggest?api-version=2019-05-06 + response: + body: + string: '{"value":[{"@search.text":"Cheapest hotel in town. Infact, a motel.","hotelId":"2"},{"@search.text":"Secret + Point Motel","hotelId":"9"}]}' + headers: + cache-control: + - no-cache + content-length: + - '137' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 20:23:41 GMT + elapsed-time: + - '138' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - 34485c62-5f1f-11ea-9ab6-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_existing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_existing.yaml new file mode 100644 index 000000000000..c825fc09261e --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_existing.yaml @@ -0,0 +1,193 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "3", "rating": 1, "@search.action": "merge"}, {"hotelId": + "4", "rating": 2, "@search.action": "merge"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 6E68CB4C62F0ACCA4B82590982C33FAA + method: POST + uri: https://search40dc1028.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"3","status":true,"errorMessage":null,"statusCode":200},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '137' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:23 GMT + elapsed-time: + - '107' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - cfaa51f4-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 6E68CB4C62F0ACCA4B82590982C33FAA + method: GET + uri: https://search40dc1028.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:21:25 GMT + elapsed-time: + - '6' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - d1a22d24-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 6E68CB4C62F0ACCA4B82590982C33FAA + method: GET + uri: https://search40dc1028.search.windows.net/indexes('drgqefsg')/docs('3')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"3","hotelName":"EconoStay","description":"Very popular + hotel in town","descriptionFr":"H\u00f4tel le plus populaire en ville","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":1,"location":{"type":"Point","coordinates":[-122.131577,46.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '432' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:25 GMT + elapsed-time: + - '9' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - d1aa4cfc-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 6E68CB4C62F0ACCA4B82590982C33FAA + method: GET + uri: https://search40dc1028.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '410' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:25 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - d1b2b82e-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_missing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_missing.yaml new file mode 100644 index 000000000000..31cd45edd0ee --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_merge_documents_missing.yaml @@ -0,0 +1,185 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 1, "@search.action": "merge"}, + {"hotelId": "4", "rating": 2, "@search.action": "merge"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '130' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 46C7FFC0CB3F538795137714E6B6BC82 + method: POST + uri: https://search30a60fb7.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":false,"errorMessage":"Document not + found.","statusCode":404},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '158' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:36 GMT + elapsed-time: + - '109' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - d835bffc-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 207 + message: Multi-Status +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 46C7FFC0CB3F538795137714E6B6BC82 + method: GET + uri: https://search30a60fb7.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF10" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:21:40 GMT + elapsed-time: + - '3' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - da2bb3a2-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 46C7FFC0CB3F538795137714E6B6BC82 + method: GET + uri: https://search30a60fb7.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '' + headers: + cache-control: + - no-cache + content-length: + - '0' + date: + - Fri, 06 Mar 2020 22:21:40 GMT + elapsed-time: + - '32' + expires: + - '-1' + pragma: + - no-cache + request-id: + - da336ba6-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 46C7FFC0CB3F538795137714E6B6BC82 + method: GET + uri: https://search30a60fb7.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '410' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:40 GMT + elapsed-time: + - '14' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - da3edee6-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_merge_or_upload_documents.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_merge_or_upload_documents.yaml new file mode 100644 index 000000000000..fcc839a0ca52 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_merge_or_upload_documents.yaml @@ -0,0 +1,192 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 1, "@search.action": "mergeOrUpload"}, + {"hotelId": "4", "rating": 2, "@search.action": "mergeOrUpload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '146' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 504838938D7B67BD717ED599FBCA99D2 + method: POST + uri: https://search50c51082.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"4","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '140' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:51 GMT + elapsed-time: + - '98' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - e053b45a-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 504838938D7B67BD717ED599FBCA99D2 + method: GET + uri: https://search50c51082.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF11" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:21:54 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - e245db44-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 504838938D7B67BD717ED599FBCA99D2 + method: GET + uri: https://search50c51082.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1000","hotelName":null,"description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":1,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '225' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:54 GMT + elapsed-time: + - '7' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - e24d9f50-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 504838938D7B67BD717ED599FBCA99D2 + method: GET + uri: https://search50c51082.search.windows.net/indexes('drgqefsg')/docs('4')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"4","hotelName":"Express Rooms","description":"Pretty good + hotel","descriptionFr":"Assez bon h\u00f4tel","category":"Budget","tags":["wifi","budget"],"parkingIncluded":true,"smokingAllowed":false,"lastRenovationDate":"1995-07-01T00:00:00Z","rating":2,"location":{"type":"Point","coordinates":[-122.131577,48.678581],"crs":{"type":"name","properties":{"name":"EPSG:4326"}}},"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '410' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:21:54 GMT + elapsed-time: + - '5' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - e255d990-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_suggest.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_suggest.yaml new file mode 100644 index 000000000000..169dbf203eec --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_suggest.yaml @@ -0,0 +1,53 @@ +interactions: +- request: + body: '{"search": "mot", "suggesterName": "sg"}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 89E2D397BD0DD8F9DFB03E9CFB6D60B3 + method: POST + uri: https://search687b091f.search.windows.net/indexes('drgqefsg')/docs/search.post.suggest?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"@search.text":"Cheapest hotel in town. Infact, a motel.","hotelId":"2"},{"@search.text":"Secret + Point Motel","hotelId":"9"}]}' + headers: + cache-control: + - no-cache + content-length: + - '137' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:22:05 GMT + elapsed-time: + - '136' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - e8a0dade-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents.yaml new file mode 100644 index 000000000000..4a882462a247 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents.yaml @@ -0,0 +1,192 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure + Inn", "@search.action": "upload"}, {"hotelId": "1001", "rating": 4, "rooms": + [], "hotelName": "Redmond Hotel", "@search.action": "upload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '217' + Content-Type: + - application/json + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 48DA7A245D1D909C2DDD5B913A209CA3 + method: POST + uri: https://searchcc370cd3.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06 + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"1001","status":true,"errorMessage":null,"statusCode":201}]}' + headers: + cache-control: + - no-cache + content-length: + - '143' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 19:51:20 GMT + elapsed-time: + - '30' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - af2e880c-5f1a-11ea-8824-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 48DA7A245D1D909C2DDD5B913A209CA3 + method: GET + uri: https://searchcc370cd3.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06 + response: + body: + string: "\uFEFF12" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Thu, 05 Mar 2020 19:51:23 GMT + elapsed-time: + - '6' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - b117c9da-5f1a-11ea-8824-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 48DA7A245D1D909C2DDD5B913A209CA3 + method: GET + uri: https://searchcc370cd3.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06 + response: + body: + string: '{"hotelId":"1000","hotelName":"Azure Inn","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":5,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '232' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 19:51:23 GMT + elapsed-time: + - '8' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - b11fea20-5f1a-11ea-8824-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-core/1.2.3 Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) azsdk-python-searchindexclient/unknown + api-key: + - 48DA7A245D1D909C2DDD5B913A209CA3 + method: GET + uri: https://searchcc370cd3.search.windows.net/indexes('drgqefsg')/docs('1001')?api-version=2019-05-06 + response: + body: + string: '{"hotelId":"1001","hotelName":"Redmond Hotel","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":4,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '236' + content-type: + - application/json; odata.metadata=none + date: + - Thu, 05 Mar 2020 19:51:23 GMT + elapsed-time: + - '3' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - b1282776-5f1a-11ea-8824-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_existing.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_existing.yaml new file mode 100644 index 000000000000..1f3298db9aa9 --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_existing.yaml @@ -0,0 +1,54 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure + Inn", "@search.action": "upload"}, {"hotelId": "3", "rating": 4, "rooms": [], + "hotelName": "Redmond Hotel", "@search.action": "upload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '214' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - B0D02AF6FB5F6B1E0935A4A2455EAC3C + method: POST + uri: https://search527b109d.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"3","status":true,"errorMessage":null,"statusCode":200}]}' + headers: + cache-control: + - no-cache + content-length: + - '140' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:22:15 GMT + elapsed-time: + - '113' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - ef09662a-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_new.yaml b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_new.yaml new file mode 100644 index 000000000000..b7212abab27d --- /dev/null +++ b/sdk/search/azure-search/tests/recordings/test_live.test_upload_documents_new.yaml @@ -0,0 +1,192 @@ +interactions: +- request: + body: '{"value": [{"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure + Inn", "@search.action": "upload"}, {"hotelId": "1001", "rating": 4, "rooms": + [], "hotelName": "Redmond Hotel", "@search.action": "upload"}]}' + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '217' + Content-Type: + - application/json + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 33E39039629D93A2152FF5B1C85309C2 + method: POST + uri: https://search3990e7c.search.windows.net/indexes('drgqefsg')/docs/search.index?api-version=2019-05-06-Preview + response: + body: + string: '{"value":[{"key":"1000","status":true,"errorMessage":null,"statusCode":201},{"key":"1001","status":true,"errorMessage":null,"statusCode":201}]}' + headers: + cache-control: + - no-cache + content-length: + - '143' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:22:27 GMT + elapsed-time: + - '89' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - f5ef950e-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 33E39039629D93A2152FF5B1C85309C2 + method: GET + uri: https://search3990e7c.search.windows.net/indexes('drgqefsg')/docs/$count?api-version=2019-05-06-Preview + response: + body: + string: "\uFEFF12" + headers: + cache-control: + - no-cache + content-length: + - '5' + content-type: + - text/plain + date: + - Fri, 06 Mar 2020 22:22:30 GMT + elapsed-time: + - '4' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - f7ebf230-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 33E39039629D93A2152FF5B1C85309C2 + method: GET + uri: https://search3990e7c.search.windows.net/indexes('drgqefsg')/docs('1000')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1000","hotelName":"Azure Inn","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":5,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '232' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:22:30 GMT + elapsed-time: + - '11' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - f7f3e602-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;odata.metadata=none + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-searchindexclient/unknown Python/3.7.3 (Darwin-19.3.0-x86_64-i386-64bit) + api-key: + - 33E39039629D93A2152FF5B1C85309C2 + method: GET + uri: https://search3990e7c.search.windows.net/indexes('drgqefsg')/docs('1001')?api-version=2019-05-06-Preview + response: + body: + string: '{"hotelId":"1001","hotelName":"Redmond Hotel","description":null,"descriptionFr":null,"category":null,"tags":[],"parkingIncluded":null,"smokingAllowed":null,"lastRenovationDate":null,"rating":4,"location":null,"address":null,"rooms":[]}' + headers: + cache-control: + - no-cache + content-length: + - '236' + content-type: + - application/json; odata.metadata=none + date: + - Fri, 06 Mar 2020 22:22:30 GMT + elapsed-time: + - '4' + expires: + - '-1' + odata-version: + - '4.0' + pragma: + - no-cache + preference-applied: + - odata.include-annotations="*" + request-id: + - f7fd32de-5ff8-11ea-b13a-8c8590507855 + strict-transport-security: + - max-age=15724800; includeSubDomains + vary: + - Accept-Encoding + status: + code: 200 + message: OK +version: 1 diff --git a/sdk/search/azure-search/tests/search_service_preparer.py b/sdk/search/azure-search/tests/search_service_preparer.py new file mode 100644 index 000000000000..1687849491f2 --- /dev/null +++ b/sdk/search/azure-search/tests/search_service_preparer.py @@ -0,0 +1,149 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +from os.path import dirname, realpath +import time + +try: + from unittest.mock import Mock +except ImportError: # python < 3.3 + from mock import Mock + +import json +import requests + +from devtools_testutils import AzureMgmtPreparer, ResourceGroupPreparer +from devtools_testutils.resource_testcase import RESOURCE_GROUP_PARAM +from azure_devtools.scenario_tests.exceptions import AzureTestError + +SERVICE_URL_FMT = "https://{}.search.windows.net/indexes?api-version=2019-05-06" + + +class SearchServicePreparer(AzureMgmtPreparer): + def __init__( + self, + schema, + index_batch=None, + name_prefix="search", + resource_group_parameter_name=RESOURCE_GROUP_PARAM, + disable_recording=True, + playback_fake_resource=None, + client_kwargs=None, + ): + super(SearchServicePreparer, self).__init__( + name_prefix, + random_name_length=24, + disable_recording=disable_recording, + playback_fake_resource=playback_fake_resource, + client_kwargs=client_kwargs, + ) + self.resource_group_parameter_name = resource_group_parameter_name + self.schema = schema + self.index_name = None + self.index_batch = index_batch + self.service_name = "TEST-SERVICE-NAME" + + def _get_resource_group(self, **kwargs): + try: + return kwargs[self.resource_group_parameter_name] + except KeyError: + template = ( + "To create a search service a resource group is required. Please add " + "decorator @{} in front of this preparer." + ) + raise AzureTestError(template.format(ResourceGroupPreparer.__name__)) + + def create_resource(self, name, **kwargs): + schema = json.loads(self.schema) + self.service_name = self.create_random_name() + self.endpoint = "https://{}.search.windows.net".format(self.service_name) + + if not self.is_live: + return { + "api_key": "api-key", + "index_name": schema["name"], + "endpoint": self.endpoint, + } + + group_name = self._get_resource_group(**kwargs).name + + from azure.mgmt.search import SearchManagementClient + from azure.mgmt.search.models import ProvisioningState + + self.mgmt_client = self.create_mgmt_client(SearchManagementClient) + + # create the search service + from azure.mgmt.search.models import SearchService, Sku + + service_config = SearchService(location="West US", sku=Sku(name="free")) + resource = self.mgmt_client.services.create_or_update( + group_name, self.service_name, service_config + ) + + retries = 4 + for i in range(retries): + try: + result = resource.result() + if result.provisioning_state == ProvisioningState.succeeded: + break + except Exception as ex: + if i == retries - 1: + raise + time.sleep(3) + time.sleep(3) + + # note the for/else here: will raise an error if we *don't* break + # above i.e. if result.provisioning state was never "Succeeded" + else: + raise AzureTestError("Could not create a search service") + + api_key = self.mgmt_client.admin_keys.get( + group_name, self.service_name + ).primary_key + + response = requests.post( + SERVICE_URL_FMT.format(self.service_name), + headers={"Content-Type": "application/json", "api-key": api_key}, + data=self.schema, + ) + if response.status_code != 201: + raise AzureTestError( + "Could not create a search index {}".format(response.status_code) + ) + self.index_name = schema["name"] + + # optionally load data into the index + if self.index_batch: + from azure.search import SearchIndexClient, SearchApiKeyCredential + from azure.search._index._generated.models import IndexBatch + + batch = IndexBatch.deserialize(self.index_batch) + index_client = SearchIndexClient( + self.endpoint, self.index_name, SearchApiKeyCredential(api_key) + ) + results = index_client.index_documents(batch) + if not all(result.succeeded for result in results): + raise AzureTestError("Document upload to search index failed") + + # Indexing is asynchronous, so if you get a 200 from the REST API, that only means that the documents are + # persisted, not that they're searchable yet. The only way to check for searchability is to run queries, + # and even then things are eventually consistent due to replication. In the Track 1 SDK tests, we "solved" + # this by using a constant delay between indexing and querying. + import time + + time.sleep(3) + + return { + "api_key": api_key, + "index_name": self.index_name, + "endpoint": self.endpoint, + } + + def remove_resource(self, name, **kwargs): + if not self.is_live: + return + + group_name = self._get_resource_group(**kwargs).name + self.mgmt_client.services.delete(group_name, self.service_name) diff --git a/sdk/search/azure-search/tests/test_index_documents_batch.py b/sdk/search/azure-search/tests/test_index_documents_batch.py new file mode 100644 index 000000000000..e382d77b83f6 --- /dev/null +++ b/sdk/search/azure-search/tests/test_index_documents_batch.py @@ -0,0 +1,71 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from azure.search._index._generated.models import IndexAction + +from azure.search import IndexDocumentsBatch + +METHOD_NAMES = [ + "add_upload_documents", + "add_delete_documents", + "add_merge_documents", + "add_merge_or_upload_documents", +] + +METHOD_MAP = dict(zip(METHOD_NAMES, ["upload", "delete", "merge", "mergeOrUpload"])) + + +class TestIndexDocumentsBatch(object): + def test_init(self): + batch = IndexDocumentsBatch() + assert batch.actions == [] + + def test_repr(self): + batch = IndexDocumentsBatch() + assert repr(batch) == "" + + batch._actions = [1, 2, 3] + assert repr(batch) == "" + + # a strict length test here would require constructing an actions list + # with a length of ~10**24, so settle for this simple sanity check on + # an extreme case. + batch_actions = list(range(2000)) + assert len(repr(batch)) <= 1024 + + def test_actions_returns_list_copy(self): + batch = IndexDocumentsBatch() + batch.actions.extend([1, 2, 3]) + assert type(batch.actions) is list + assert batch.actions == [] + assert batch.actions is not batch._actions + + @pytest.mark.parametrize("method_name", METHOD_NAMES) + def test_add_method(self, method_name): + batch = IndexDocumentsBatch() + + method = getattr(batch, method_name) + + method("doc1") + assert len(batch.actions) == 1 + + method("doc2", "doc3") + assert len(batch.actions) == 3 + + method(["doc4", "doc5"]) + assert len(batch.actions) == 5 + + method(("doc6", "doc7")) + assert len(batch.actions) == 7 + + assert all( + action.action_type == METHOD_MAP[method_name] for action in batch.actions + ) + assert all(type(action) == IndexAction for action in batch.actions) + + expected = ["doc{}".format(i) for i in range(1, 8)] + assert [action.additional_properties for action in batch.actions] == expected diff --git a/sdk/search/azure-search/tests/test_live.py b/sdk/search/azure-search/tests/test_live.py new file mode 100644 index 000000000000..13d9d2febc06 --- /dev/null +++ b/sdk/search/azure-search/tests/test_live.py @@ -0,0 +1,281 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# -------------------------------------------------------------------------- +import json +from os.path import dirname, join, realpath +import time + +import pytest + +from devtools_testutils import AzureMgmtTestCase, ResourceGroupPreparer + +from search_service_preparer import SearchServicePreparer + +CWD = dirname(realpath(__file__)) + +SCHEMA = open(join(CWD, "hotel_schema.json")).read() +BATCH = json.load(open(join(CWD, "hotel_small.json"))) + +from azure.core.exceptions import HttpResponseError +from azure.search import ( + AutocompleteQuery, + SearchIndexClient, + SearchApiKeyCredential, + SearchQuery, + SuggestQuery, +) + + +class SearchIndexClientTest(AzureMgmtTestCase): + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_get_document_count(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + assert client.get_document_count() == 10 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_get_document(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + for hotel_id in range(1, 11): + result = client.get_document(key=str(hotel_id)) + expected = BATCH["value"][hotel_id - 1] + assert result.get("hotelId") == expected.get("hotelId") + assert result.get("hotelName") == expected.get("hotelName") + assert result.get("description") == expected.get("description") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_get_document_missing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + with pytest.raises(HttpResponseError): + client.get_document(key="1000") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_get_search_simple(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = list(client.search(query="hotel")) + assert len(results) == 7 + + results = list(client.search(query="motel")) + assert len(results) == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_get_search_filter(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + + query = SearchQuery(search_text="WiFi") + query.filter("category eq 'Budget'") + query.select("hotelName", "category", "description") + query.order_by("hotelName desc") + + results = list(client.search(query=query)) + assert [x["hotelName"] for x in results] == sorted( + [x["hotelName"] for x in results], reverse=True + ) + expected = { + "category", + "hotelName", + "description", + "@search.score", + "@search.highlights", + } + assert all(set(x) == expected for x in results) + assert all(x["category"] == "Budget" for x in results) + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_autocomplete(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + query = AutocompleteQuery(search_text="mot", suggester_name="sg") + results = client.autocomplete(query=query) + assert results == [{"text": "motel", "query_plus_text": "motel"}] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_suggest(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + query = SuggestQuery(search_text="mot", suggester_name="sg") + results = client.suggest(query=query) + assert results == [ + {"hotelId": "2", "text": "Cheapest hotel in town. Infact, a motel."}, + {"hotelId": "9", "text": "Secret Point Motel"}, + ] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_upload_documents_new(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + DOCUMENTS = [ + {"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure Inn"}, + {"hotelId": "1001", "rating": 4, "rooms": [], "hotelName": "Redmond Hotel"}, + ] + results = client.upload_documents(DOCUMENTS) + assert len(results) == 2 + assert set(x.status_code for x in results) == {201} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 12 + for doc in DOCUMENTS: + result = client.get_document(key=doc["hotelId"]) + assert result["hotelId"] == doc["hotelId"] + assert result["hotelName"] == doc["hotelName"] + assert result["rating"] == doc["rating"] + assert result["rooms"] == doc["rooms"] + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_upload_documents_existing( + self, api_key, endpoint, index_name, **kwargs + ): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + DOCUMENTS = [ + {"hotelId": "1000", "rating": 5, "rooms": [], "hotelName": "Azure Inn"}, + {"hotelId": "3", "rating": 4, "rooms": [], "hotelName": "Redmond Hotel"}, + ] + results = client.upload_documents(DOCUMENTS) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 201} + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_delete_documents_existing( + self, api_key, endpoint, index_name, **kwargs + ): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = client.delete_documents([{"hotelId": "3"}, {"hotelId": "4"}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 8 + + with pytest.raises(HttpResponseError): + client.get_document(key="3") + + with pytest.raises(HttpResponseError): + client.get_document(key="4") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_delete_documents_missing( + self, api_key, endpoint, index_name, **kwargs + ): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = client.delete_documents([{"hotelId": "1000"}, {"hotelId": "4"}]) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 9 + + with pytest.raises(HttpResponseError): + client.get_document(key="1000") + + with pytest.raises(HttpResponseError): + client.get_document(key="4") + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_merge_documents_existing( + self, api_key, endpoint, index_name, **kwargs + ): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = client.merge_documents( + [{"hotelId": "3", "rating": 1}, {"hotelId": "4", "rating": 2}] + ) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 10 + + result = client.get_document(key="3") + assert result["rating"] == 1 + + result = client.get_document(key="4") + assert result["rating"] == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_merge_documents_missing(self, api_key, endpoint, index_name, **kwargs): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = client.merge_documents( + [{"hotelId": "1000", "rating": 1}, {"hotelId": "4", "rating": 2}] + ) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 404} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 10 + + with pytest.raises(HttpResponseError): + client.get_document(key="1000") + + result = client.get_document(key="4") + assert result["rating"] == 2 + + @ResourceGroupPreparer(random_name_enabled=True) + @SearchServicePreparer(schema=SCHEMA, index_batch=BATCH) + def test_merge_or_upload_documents( + self, api_key, endpoint, index_name, **kwargs + ): + client = SearchIndexClient( + endpoint, index_name, SearchApiKeyCredential(api_key) + ) + results = client.merge_or_upload_documents( + [{"hotelId": "1000", "rating": 1}, {"hotelId": "4", "rating": 2}] + ) + assert len(results) == 2 + assert set(x.status_code for x in results) == {200, 201} + + # There can be some lag before a document is searchable + time.sleep(3) + + assert client.get_document_count() == 11 + + result = client.get_document(key="1000") + assert result["rating"] == 1 + + result = client.get_document(key="4") + assert result["rating"] == 2 diff --git a/sdk/search/azure-search/tests/test_queries.py b/sdk/search/azure-search/tests/test_queries.py new file mode 100644 index 000000000000..0e03a98d624d --- /dev/null +++ b/sdk/search/azure-search/tests/test_queries.py @@ -0,0 +1,167 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +try: + from unittest import mock +except ImportError: + import mock + +from azure.search._index._generated.models import ( + AutocompleteRequest, + SearchRequest, + SuggestRequest, +) + +from azure.search import AutocompleteQuery, SearchQuery, SuggestQuery + + +class TestAutocompleteQuery(object): + def test_init(self): + query = AutocompleteQuery(search_text="text", suggester_name="sg") + assert type(query.request) is AutocompleteRequest + assert query.request.filter is None + + @mock.patch("azure.search.AutocompleteQuery._request_type") + def test_kwargs_forwarded(self, mock_request): + mock_request.return_value = None + AutocompleteQuery(foo=10, bar=20) + assert mock_request.called + assert mock_request.call_args[0] == () + assert mock_request.call_args[1] == {"foo": 10, "bar": 20} + + def test_repr(self): + query = AutocompleteQuery(search_text="foo bar", suggester_name="sg") + assert repr(query) == "" + + query = AutocompleteQuery(search_text="aaaaabbbbb" * 200, suggester_name="sg") + assert len(repr(query)) == 1024 + + def test_filter(self): + query = AutocompleteQuery(search_text="text", suggester_name="sg") + assert query.request.filter is None + query.filter("expr0") + assert query.request.filter == "expr0" + + query = AutocompleteQuery( + search_text="text", suggester_name="sg", filter="expr1" + ) + assert query.request.filter == "expr1" + query.filter("expr2") + assert query.request.filter == "expr2" + + +class TestSearchQuery(object): + def test_init(self): + query = SearchQuery() + assert type(query.request) is SearchRequest + assert query.request.filter is None + assert query.request.order_by is None + assert query.request.select is None + + @mock.patch("azure.search.SearchQuery._request_type") + def test_kwargs_forwarded(self, mock_request): + mock_request.return_value = None + SearchQuery(foo=10, bar=20) + assert mock_request.called + assert mock_request.call_args[0] == () + assert mock_request.call_args[1] == {"foo": 10, "bar": 20} + + def test_repr(self): + query = SearchQuery() + assert repr(query) == "" + + query = SearchQuery(search_text="foo bar", suggester_name="sg") + assert repr(query) == "" + + query = SearchQuery(search_text="aaaaabbbbb" * 200) + assert len(repr(query)) == 1024 + + def test_filter(self): + query = SearchQuery() + assert query.request.filter is None + query.filter("expr0") + assert query.request.filter == "expr0" + + query = SearchQuery(filter="expr0") + assert query.request.filter == "expr0" + query.filter("expr1") + assert query.request.filter == "expr1" + + def test_order_by(self): + query = SearchQuery() + assert query.request.order_by is None + query.order_by("f0") + assert query.request.order_by == "f0" + query.order_by("f1,f2") + assert query.request.order_by == "f1,f2" + query.order_by("f3", "f4") + assert query.request.order_by == "f3,f4" + + query = SearchQuery(order_by="f0") + assert query.request.order_by == "f0" + query.order_by("f1,f2") + assert query.request.order_by == "f1,f2" + query.order_by("f3", "f4") + assert query.request.order_by == "f3,f4" + + with pytest.raises(ValueError) as e: + query.order_by() + assert str(e) == "At least one field must be provided" + + def test_select(self): + query = SearchQuery() + assert query.request.select is None + query.select("f0") + assert query.request.select == "f0" + query.select("f1,f2") + assert query.request.select == "f1,f2" + query.select("f3", "f4") + assert query.request.select == "f3,f4" + + query = SearchQuery(select="f0") + assert query.request.select == "f0" + query.select("f1,f2") + assert query.request.select == "f1,f2" + query.select("f3", "f4") + assert query.request.select == "f3,f4" + + with pytest.raises(ValueError) as e: + query.select() + assert str(e) == "At least one field must be provided" + + +class TestSuggestQuery(object): + def test_init(self): + query = SuggestQuery(search_text="text", suggester_name="sg") + assert type(query.request) is SuggestRequest + assert query.request.filter is None + + @mock.patch("azure.search.SuggestQuery._request_type") + def test_kwargs_forwarded(self, mock_request): + mock_request.return_value = None + SuggestQuery(foo=10, bar=20) + assert mock_request.called + assert mock_request.call_args[0] == () + assert mock_request.call_args[1] == {"foo": 10, "bar": 20} + + def test_repr(self): + query = SuggestQuery(search_text="foo bar", suggester_name="sg") + assert repr(query) == "" + + query = SuggestQuery(search_text="aaaaabbbbb" * 200, suggester_name="sg") + assert len(repr(query)) == 1024 + + def test_filter(self): + query = SuggestQuery(search_text="text", suggester_name="sg") + assert query.request.filter is None + query.filter("expr0") + assert query.request.filter == "expr0" + + query = SuggestQuery(search_text="text", suggester_name="sg", filter="expr1") + assert query.request.filter == "expr1" + query.filter("expr2") + assert query.request.filter == "expr2" diff --git a/sdk/search/azure-search/tests/test_search_api_key_credential.py b/sdk/search/azure-search/tests/test_search_api_key_credential.py new file mode 100644 index 000000000000..24d2f8216c33 --- /dev/null +++ b/sdk/search/azure-search/tests/test_search_api_key_credential.py @@ -0,0 +1,28 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +from azure.search import SearchApiKeyCredential + +BAD_KEYS = (10, 10.2, None, dict(), set(), list()) + + +class TestSearchApiKeyCredential(object): + def test_init(self): + credential = SearchApiKeyCredential("some_key") + assert credential.api_key == "some_key" + + @pytest.mark.parametrize("bad_key", BAD_KEYS, ids=repr) + def test_bad_init(self, bad_key): + with pytest.raises(TypeError) as e: + SearchApiKeyCredential(bad_key) + assert str(e) == "api_key must be a string." + + def test_update_key(self): + credential = SearchApiKeyCredential("some_key") + assert credential.api_key == "some_key" + credential.update_key("new_key") + assert credential.api_key == "new_key" diff --git a/sdk/search/azure-search/tests/test_search_index_client.py b/sdk/search/azure-search/tests/test_search_index_client.py new file mode 100644 index 000000000000..0bb5f06f05f1 --- /dev/null +++ b/sdk/search/azure-search/tests/test_search_index_client.py @@ -0,0 +1,232 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + +import pytest + +try: + from unittest import mock +except ImportError: + import mock + +from azure.core.paging import ItemPaged + +from azure.search._index._generated.models import ( + IndexAction, + IndexBatch, + SearchDocumentsResult, + SearchResult, +) +from azure.search._index._search_index_client import _SearchDocumentsPaged + +from azure.search import ( + AutocompleteQuery, + IndexDocumentsBatch, + SearchApiKeyCredential, + SearchIndexClient, + SearchQuery, + SuggestQuery, + odata, +) + +CREDENTIAL = SearchApiKeyCredential(api_key="test_api_key") + +CRUD_METHOD_NAMES = [ + "upload_documents", + "delete_documents", + "merge_documents", + "merge_or_upload_documents", +] + +CRUD_METHOD_MAP = dict( + zip(CRUD_METHOD_NAMES, ["upload", "delete", "merge", "mergeOrUpload"]) +) + +ENDPOINT = "https://" + +class Test_odata(object): + def test_const(self): + assert odata("no escapes") == "no escapes" + + def test_numbers(self): + assert odata("foo eq {foo}", foo=10) == "foo eq 10" + + def test_string(self): + assert odata("foo eq {foo}", foo="a string") == "foo eq 'a string'" + + def test_mixed(self): + expected = "foo eq 'a string' and bar le 10" + out = odata("foo eq {foo} and bar le {bar}", foo="a string", bar=10) + assert out == expected + + def test_escape_single_quote(self): + assert odata("foo eq {foo}", foo="a '' str'ing") == "foo eq 'a '''' str''ing'" + + def test_prevent_double_quoting(self): + assert odata("foo eq '{foo}'", foo="a string") == "foo eq 'a string'" + + +class TestSearchIndexClient(object): + def test_init(self): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + assert client._client._config.headers_policy.headers == { + "api-key": "test_api_key", + "Accept": "application/json;odata.metadata=none", + } + + def test_repr(self): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + assert repr(client) == "".format( + repr("endpoint"), repr("index name") + ) + + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.count" + ) + def test_get_document_count(self, mock_count): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + client.get_document_count() + assert mock_count.called + assert mock_count.call_args[0] == () + assert mock_count.call_args[1] == {} + + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.get" + ) + def test_get_document_count(self, mock_get): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + client.get_document("some_key") + assert mock_get.called + assert mock_get.call_args[0] == () + assert mock_get.call_args[1] == {"key": "some_key", "selected_fields": None} + + mock_get.reset() + + client.get_document("some_key", selected_fields="foo") + assert mock_get.called + assert mock_get.call_args[0] == () + assert mock_get.call_args[1] == {"key": "some_key", "selected_fields": "foo"} + + @pytest.mark.parametrize( + "query", ["search text", SearchQuery(search_text="search text")], ids=repr + ) + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.search_post" + ) + def test_search_query_argument(self, mock_search_post, query): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + result = client.search(query) + assert isinstance(result, ItemPaged) + assert result._page_iterator_class is _SearchDocumentsPaged + search_result = SearchDocumentsResult() + search_result.results = [SearchResult(additional_properties={"key": "val"})] + mock_search_post.return_value = search_result + assert not mock_search_post.called + next(result) + assert mock_search_post.called + assert mock_search_post.call_args[0] == () + assert ( + mock_search_post.call_args[1]["search_request"].search_text == "search text" + ) + + def test_search_bad_argument(self): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + with pytest.raises(TypeError) as e: + client.search(10) + assert str(e) == "Expected a SuggestQuery for 'query', but got {}".format( + repr(10) + ) + + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.suggest_post" + ) + def test_suggest_query_argument(self, mock_suggest_post): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + result = client.suggest( + SuggestQuery(search_text="search text", suggester_name="sg") + ) + assert mock_suggest_post.called + assert mock_suggest_post.call_args[0] == () + assert ( + mock_suggest_post.call_args[1]["suggest_request"].search_text + == "search text" + ) + + def test_suggest_bad_argument(self): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + with pytest.raises(TypeError) as e: + client.suggest("bad_query") + assert str(e) == "Expected a SuggestQuery for 'query', but got {}".format( + repr("bad_query") + ) + + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.autocomplete_post" + ) + def test_autocomplete_query_argument(self, mock_autocomplete_post): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + result = client.autocomplete( + AutocompleteQuery(search_text="search text", suggester_name="sg") + ) + assert mock_autocomplete_post.called + assert mock_autocomplete_post.call_args[0] == () + assert ( + mock_autocomplete_post.call_args[1]["autocomplete_request"].search_text + == "search text" + ) + + def test_autocomplete_bad_argument(self): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + with pytest.raises(TypeError) as e: + client.autocomplete("bad_query") + assert str( + e + ) == "Expected a AutocompleteQuery for 'query', but got {}".format( + repr("bad_query") + ) + + @pytest.mark.parametrize( + "arg", [[], ["doc1"], ["doc1", "doc2"]], ids=lambda x: str(len(x)) + " docs" + ) + @pytest.mark.parametrize("method_name", CRUD_METHOD_NAMES) + def test_add_method(self, arg, method_name): + with mock.patch.object( + SearchIndexClient, "index_documents", return_value=None + ) as mock_index_documents: + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + + method = getattr(client, method_name) + method(arg, extra="foo") + + assert mock_index_documents.called + assert len(mock_index_documents.call_args[0]) == 1 + batch = mock_index_documents.call_args[0][0] + assert isinstance(batch, IndexDocumentsBatch) + assert all( + action.action_type == CRUD_METHOD_MAP[method_name] + for action in batch.actions + ) + assert [action.additional_properties for action in batch.actions] == arg + assert mock_index_documents.call_args[1] == {"extra": "foo"} + + @mock.patch( + "azure.search._index._generated.operations._documents_operations.DocumentsOperations.index" + ) + def test_index_documents(self, mock_index): + client = SearchIndexClient("endpoint", "index name", CREDENTIAL) + + batch = IndexDocumentsBatch() + batch.add_upload_documents("upload1") + batch.add_delete_documents("delete1", "delete2") + batch.add_merge_documents(["merge1", "merge2", "merge3"]) + batch.add_merge_or_upload_documents("merge_or_upload1") + + client.index_documents(batch, extra="foo") + assert mock_index.called + assert mock_index.call_args[0] == () + assert len(mock_index.call_args[1]) == 2 + assert mock_index.call_args[1]["extra"] == "foo" + index_documents = mock_index.call_args[1]["batch"] + assert isinstance(index_documents, IndexBatch) + assert index_documents.actions == batch.actions diff --git a/sdk/search/ci.yml b/sdk/search/ci.yml index 6dc55b43b393..d4f2b9e891ac 100644 --- a/sdk/search/ci.yml +++ b/sdk/search/ci.yml @@ -39,6 +39,8 @@ stages: parameters: ServiceDirectory: search Artifacts: + - name: azure_search_index + safeName: azuresearchindex - name: azure_mgmt_search safeName: azuremgmtsearch - + diff --git a/sdk/search/tests.yml b/sdk/search/tests.yml new file mode 100644 index 000000000000..24c949ae1ce0 --- /dev/null +++ b/sdk/search/tests.yml @@ -0,0 +1,20 @@ +trigger: none + +resources: + repositories: + - repository: azure-sdk-tools + type: github + name: Azure/azure-sdk-tools + endpoint: azure + +jobs: + - template: ../../eng/pipelines/templates/jobs/archetype-sdk-tests.yml + parameters: + BuildTargetingString: $(BuildTargetingString) + ServiceDirectory: search + EnvVars: + AZURE_CLIENT_ID: $(aad-azure-sdk-test-client-id) + AZURE_CLIENT_SECRET: $(aad-azure-sdk-test-client-secret) + AZURE_TENANT_ID: $(aad-azure-sdk-test-tenant-id) + AZURE_CLIENT_OID: $(aad-azure-sdk-test-client-oid) + TEST_MODE: 'RunLiveNoRecord' \ No newline at end of file diff --git a/shared_requirements.txt b/shared_requirements.txt index b8d6c4eea742..07ac3f4a95ab 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -136,4 +136,6 @@ opentelemetry-api==0.4a0 #override azure-storage-queue azure-core<2.0.0,>=1.2.2 #override azure-storage-file-share azure-core<2.0.0,>=1.2.2 #override azure-storage-file-datalake azure-core<2.0.0,>=1.2.2 -#override azure-servicebus uamqp>=1.2.5,<2.0.0 \ No newline at end of file +#override azure-servicebus uamqp>=1.2.5,<2.0.0 +#override azure-search msrest>=0.6.10 +#override azure-search azure-core<2.0.0,>=1.2.2 \ No newline at end of file