|
5 | 5 | "id": "aba4346f", |
6 | 6 | "metadata": {}, |
7 | 7 | "source": [ |
8 | | - "## Document Permissions in Azure AI Search\n", |
| 8 | + "## Document Permissions in Azure AI Search" |
| 9 | + ] |
| 10 | + }, |
| 11 | + { |
| 12 | + "cell_type": "code", |
| 13 | + "execution_count": 10, |
| 14 | + "id": "0b40bb5b", |
| 15 | + "metadata": {}, |
| 16 | + "outputs": [], |
| 17 | + "source": [ |
| 18 | + "from dotenv import load_dotenv\n", |
| 19 | + "from azure.identity import DefaultAzureCredential, get_bearer_token_provider\n", |
| 20 | + "import os\n", |
| 21 | + "\n", |
| 22 | + "load_dotenv(override=True) # take environment variables from .env.\n", |
| 23 | + "\n", |
| 24 | + "# The following variables from your .env file are used in this notebook\n", |
| 25 | + "endpoint = os.environ[\"AZURE_SEARCH_ENDPOINT\"]\n", |
| 26 | + "credential = DefaultAzureCredential()\n", |
| 27 | + "index_name = os.getenv(\"AZURE_SEARCH_INDEX\", \"document-permissions-sample\")\n", |
| 28 | + "indexer_name = os.getenv(\"AZURE_SEARCH_INDEXER\", \"document-permissions-sample-indexer\")\n", |
| 29 | + "datasource_name = os.getenv(\"AZURE_SEARCH_DATASOURCE\", \"document-permissions-sample-datasource\")\n", |
| 30 | + "adls_gen2_account_name = os.getenv(\"AZURE_STORAGE_ACCOUNT_NAME\", \"documentpermissionssample\")\n", |
| 31 | + "adls_gen2_container_name = os.getenv(\"AZURE_STORAGE_CONTAINER_NAME\", \"documentpermissionssample\")\n", |
| 32 | + "adls_gen2_connection_string = os.environ[\"AZURE_STORAGE_CONNECTION_STRING\"]\n", |
| 33 | + "token_provider = get_bearer_token_provider(credential, \"https://search.azure.com/.default\")" |
| 34 | + ] |
| 35 | + }, |
| 36 | + { |
| 37 | + "cell_type": "code", |
| 38 | + "execution_count": 2, |
| 39 | + "id": "2f981cad", |
| 40 | + "metadata": {}, |
| 41 | + "outputs": [ |
| 42 | + { |
| 43 | + "name": "stdout", |
| 44 | + "output_type": "stream", |
| 45 | + "text": [ |
| 46 | + "Index 'document-permissions-sample' created with permission filter option enabled.\n" |
| 47 | + ] |
| 48 | + } |
| 49 | + ], |
| 50 | + "source": [ |
| 51 | + "from azure.search.documents.indexes.models import SearchField, SearchIndex, PermissionFilter, SearchIndexPermissionFilterOption\n", |
| 52 | + "from azure.search.documents.indexes import SearchIndexClient\n", |
| 53 | + "\n", |
| 54 | + "index_client = SearchIndexClient(endpoint=endpoint, credential=credential)\n", |
| 55 | + "index = SearchIndex(\n", |
| 56 | + " name=index_name,\n", |
| 57 | + " fields=[\n", |
| 58 | + " SearchField(name=\"id\", type=\"Edm.String\", key=True, filterable=True, sortable=True),\n", |
| 59 | + " SearchField(name=\"oid\", type=\"Collection(Edm.String)\", filterable=True, permission_filter=PermissionFilter.USER_IDS),\n", |
| 60 | + " SearchField(name=\"group\", type=\"Collection(Edm.String)\", filterable=True, permission_filter=PermissionFilter.GROUP_IDS),\n", |
| 61 | + " SearchField(name=\"metadata_storage_path\", type=\"Edm.String\", searchable=True),\n", |
| 62 | + " SearchField(name=\"metadata_storage_name\", type=\"Edm.String\", searchable=True)\n", |
| 63 | + " ],\n", |
| 64 | + " permission_filter_option=SearchIndexPermissionFilterOption.ENABLED\n", |
| 65 | + ")\n", |
| 66 | + "\n", |
| 67 | + "index_client.create_or_update_index(index=index)\n", |
| 68 | + "print(f\"Index '{index_name}' created with permission filter option enabled.\")" |
| 69 | + ] |
| 70 | + }, |
| 71 | + { |
| 72 | + "cell_type": "code", |
| 73 | + "execution_count": 11, |
| 74 | + "id": "b25aaf7b", |
| 75 | + "metadata": {}, |
| 76 | + "outputs": [ |
| 77 | + { |
| 78 | + "name": "stdout", |
| 79 | + "output_type": "stream", |
| 80 | + "text": [ |
| 81 | + "Datasource 'document-permissions-sample-datasource' created with permission filter option enabled.\n" |
| 82 | + ] |
| 83 | + } |
| 84 | + ], |
| 85 | + "source": [ |
| 86 | + "from azure.search.documents.indexes.models import SearchIndexerDataSourceConnection, SearchIndexerDataSourceType, IndexerPermissionOption, SearchIndexerDataContainer, DataSourceCredentials\n", |
| 87 | + "from azure.search.documents.indexes import SearchIndexerClient\n", |
| 88 | + "indexer_client = SearchIndexerClient(endpoint=endpoint, credential=credential)\n", |
| 89 | + "datasource = SearchIndexerDataSourceConnection(\n", |
| 90 | + " name=datasource_name,\n", |
| 91 | + " type=SearchIndexerDataSourceType.ADLS_GEN2,\n", |
| 92 | + " connection_string=adls_gen2_connection_string,\n", |
| 93 | + " container=SearchIndexerDataContainer(name=adls_gen2_container_name),\n", |
| 94 | + " indexer_permission_options=[IndexerPermissionOption.GROUP_IDS]\n", |
| 95 | + ")\n", |
| 96 | + "\n", |
| 97 | + "indexer_client.create_or_update_data_source_connection(datasource)\n", |
| 98 | + "print(f\"Datasource '{datasource_name}' created with permission filter option enabled.\")" |
| 99 | + ] |
| 100 | + }, |
| 101 | + { |
| 102 | + "cell_type": "code", |
| 103 | + "execution_count": null, |
| 104 | + "id": "2ce7eb5e", |
| 105 | + "metadata": {}, |
| 106 | + "outputs": [ |
| 107 | + { |
| 108 | + "ename": "HttpResponseError", |
| 109 | + "evalue": "() Field mapping specifies target field 'metadata_group_ids' that is not present in the target index\nCode: \nMessage: Field mapping specifies target field 'metadata_group_ids' that is not present in the target index", |
| 110 | + "output_type": "error", |
| 111 | + "traceback": [ |
| 112 | + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", |
| 113 | + "\u001b[31mHttpResponseError\u001b[39m Traceback (most recent call last)", |
| 114 | + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 13\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mazure\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01msearch\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mdocuments\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mindexes\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mmodels\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m SearchIndexer, FieldMapping\n\u001b[32m 3\u001b[39m indexer = SearchIndexer(\n\u001b[32m 4\u001b[39m name=indexer_name,\n\u001b[32m 5\u001b[39m target_index_name=index_name,\n\u001b[32m (...)\u001b[39m\u001b[32m 10\u001b[39m ]\n\u001b[32m 11\u001b[39m )\n\u001b[32m---> \u001b[39m\u001b[32m13\u001b[39m \u001b[43mindexer_client\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcreate_or_update_indexer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mindexer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 14\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mIndexer \u001b[39m\u001b[33m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mindexer_name\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m\u001b[33m created\u001b[39m\u001b[33m\"\u001b[39m)\n", |
| 115 | + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\magottei\\source\\azure-search-python-samples-pr\\.venv\\Lib\\site-packages\\azure\\core\\tracing\\decorator.py:119\u001b[39m, in \u001b[36mdistributed_trace.<locals>.decorator.<locals>.wrapper_use_tracer\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 117\u001b[39m \u001b[38;5;66;03m# If tracing is disabled globally and user didn't explicitly enable it, don't trace.\u001b[39;00m\n\u001b[32m 118\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m user_enabled \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m tracing_enabled \u001b[38;5;129;01mand\u001b[39;00m user_enabled \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m--> \u001b[39m\u001b[32m119\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 121\u001b[39m \u001b[38;5;66;03m# Merge span is parameter is set, but only if no explicit parent are passed\u001b[39;00m\n\u001b[32m 122\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m merge_span \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m passed_in_parent:\n", |
| 116 | + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\magottei\\source\\azure-search-python-samples-pr\\.venv\\Lib\\site-packages\\azure\\search\\documents\\indexes\\_search_indexer_client.py:140\u001b[39m, in \u001b[36mSearchIndexerClient.create_or_update_indexer\u001b[39m\u001b[34m(self, indexer, match_condition, skip_indexer_reset_requirement_for_cache, disable_cache_reprocessing_change_detection, **kwargs)\u001b[39m\n\u001b[32m 138\u001b[39m name = indexer.name\n\u001b[32m 139\u001b[39m patched_indexer = indexer._to_generated() \u001b[38;5;66;03m# pylint:disable=protected-access\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m140\u001b[39m result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_client\u001b[49m\u001b[43m.\u001b[49m\u001b[43mindexers\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcreate_or_update\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 141\u001b[39m \u001b[43m \u001b[49m\u001b[43mindexer_name\u001b[49m\u001b[43m=\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 142\u001b[39m \u001b[43m \u001b[49m\u001b[43mindexer\u001b[49m\u001b[43m=\u001b[49m\u001b[43mpatched_indexer\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 143\u001b[39m \u001b[43m \u001b[49m\u001b[43mprefer\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mreturn=representation\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 144\u001b[39m \u001b[43m \u001b[49m\u001b[43merror_map\u001b[49m\u001b[43m=\u001b[49m\u001b[43merror_map\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 145\u001b[39m \u001b[43m \u001b[49m\u001b[43mskip_indexer_reset_requirement_for_cache\u001b[49m\u001b[43m=\u001b[49m\u001b[43mskip_indexer_reset_requirement_for_cache\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 146\u001b[39m \u001b[43m \u001b[49m\u001b[43mdisable_cache_reprocessing_change_detection\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdisable_cache_reprocessing_change_detection\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 147\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\n\u001b[32m 148\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 149\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m cast(SearchIndexer, SearchIndexer._from_generated(result))\n", |
| 117 | + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\magottei\\source\\azure-search-python-samples-pr\\.venv\\Lib\\site-packages\\azure\\core\\tracing\\decorator.py:119\u001b[39m, in \u001b[36mdistributed_trace.<locals>.decorator.<locals>.wrapper_use_tracer\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 117\u001b[39m \u001b[38;5;66;03m# If tracing is disabled globally and user didn't explicitly enable it, don't trace.\u001b[39;00m\n\u001b[32m 118\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m user_enabled \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m \u001b[38;5;129;01mor\u001b[39;00m (\u001b[38;5;129;01mnot\u001b[39;00m tracing_enabled \u001b[38;5;129;01mand\u001b[39;00m user_enabled \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m--> \u001b[39m\u001b[32m119\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 121\u001b[39m \u001b[38;5;66;03m# Merge span is parameter is set, but only if no explicit parent are passed\u001b[39;00m\n\u001b[32m 122\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m merge_span \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m passed_in_parent:\n", |
| 118 | + "\u001b[36mFile \u001b[39m\u001b[32mc:\\Users\\magottei\\source\\azure-search-python-samples-pr\\.venv\\Lib\\site-packages\\azure\\search\\documents\\indexes\\_generated\\operations\\_indexers_operations.py:981\u001b[39m, in \u001b[36mIndexersOperations.create_or_update\u001b[39m\u001b[34m(self, indexer_name, prefer, indexer, if_match, if_none_match, skip_indexer_reset_requirement_for_cache, disable_cache_reprocessing_change_detection, request_options, **kwargs)\u001b[39m\n\u001b[32m 979\u001b[39m map_error(status_code=response.status_code, response=response, error_map=error_map)\n\u001b[32m 980\u001b[39m error = \u001b[38;5;28mself\u001b[39m._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response)\n\u001b[32m--> \u001b[39m\u001b[32m981\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HttpResponseError(response=response, model=error)\n\u001b[32m 983\u001b[39m deserialized = \u001b[38;5;28mself\u001b[39m._deserialize(\u001b[33m\"\u001b[39m\u001b[33mSearchIndexer\u001b[39m\u001b[33m\"\u001b[39m, pipeline_response.http_response)\n\u001b[32m 985\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mcls\u001b[39m:\n", |
| 119 | + "\u001b[31mHttpResponseError\u001b[39m: () Field mapping specifies target field 'metadata_group_ids' that is not present in the target index\nCode: \nMessage: Field mapping specifies target field 'metadata_group_ids' that is not present in the target index" |
| 120 | + ] |
| 121 | + } |
| 122 | + ], |
| 123 | + "source": [ |
| 124 | + "from azure.search.documents.indexes.models import SearchIndexer, FieldMapping\n", |
| 125 | + "\n", |
| 126 | + "indexer = SearchIndexer(\n", |
| 127 | + " name=indexer_name,\n", |
| 128 | + " target_index_name=index_name,\n", |
| 129 | + " data_source_name=datasource_name,\n", |
| 130 | + " field_mappings=[\n", |
| 131 | + " FieldMapping(source_field_name=\"metadata_group_ids\", target_field_name=\"group\"),\n", |
| 132 | + " FieldMapping(source_field_name=\"metadata_user_ids\", target_field_name=\"oid\"),\n", |
| 133 | + " ]\n", |
| 134 | + ")\n", |
9 | 135 | "\n", |
10 | | - "1. Setup index w/ permissions\n", |
11 | | - "1. Setup data source w/ permissions\n", |
12 | | - "1. Setup indexer\n", |
13 | | - "1. Connect to App\n", |
14 | | - "1. Setup authentication\n", |
15 | | - "1. Show how to query\n", |
16 | | - "1. Show pushing data with permissions into index" |
| 136 | + "indexer_client.create_or_update_indexer(indexer)\n", |
| 137 | + "print(f\"Indexer '{indexer_name}' created\")\n" |
17 | 138 | ] |
18 | 139 | } |
19 | 140 | ], |
20 | 141 | "metadata": { |
| 142 | + "kernelspec": { |
| 143 | + "display_name": ".venv", |
| 144 | + "language": "python", |
| 145 | + "name": "python3" |
| 146 | + }, |
21 | 147 | "language_info": { |
22 | | - "name": "python" |
| 148 | + "codemirror_mode": { |
| 149 | + "name": "ipython", |
| 150 | + "version": 3 |
| 151 | + }, |
| 152 | + "file_extension": ".py", |
| 153 | + "mimetype": "text/x-python", |
| 154 | + "name": "python", |
| 155 | + "nbconvert_exporter": "python", |
| 156 | + "pygments_lexer": "ipython3", |
| 157 | + "version": "3.12.10" |
23 | 158 | } |
24 | 159 | }, |
25 | 160 | "nbformat": 4, |
|
0 commit comments