├── .circleci ├── config.yml └── retrieve-credentials.sh ├── .gitignore ├── .php_cs.dist ├── AbstractClient.php ├── Client.php ├── ClientBuilder.php ├── Connection └── Handler │ ├── ApiErrorHandler.php │ ├── RateLimitLoggingHandler.php │ ├── RequestAuthenticationHandler.php │ └── RequestClientHeaderHandler.php ├── Dockerfile ├── Endpoint ├── AddMetaEngineSource.php ├── CreateCuration.php ├── CreateSynonymSet.php ├── DeleteCuration.php ├── DeleteDocuments.php ├── DeleteEngine.php ├── DeleteMetaEngineSource.php ├── DeleteSynonymSet.php ├── DoCreateEngine.php ├── GetApiLogs.php ├── GetCountAnalytics.php ├── GetCuration.php ├── GetDocuments.php ├── GetEngine.php ├── GetSchema.php ├── GetSearchSettings.php ├── GetSynonymSet.php ├── GetTopClicksAnalytics.php ├── GetTopQueriesAnalytics.php ├── IndexDocuments.php ├── ListCurations.php ├── ListDocuments.php ├── ListEngines.php ├── ListSynonymSets.php ├── LogClickthrough.php ├── MultiSearch.php ├── QuerySuggestion.php ├── ResetSearchSettings.php ├── Search.php ├── UpdateCuration.php ├── UpdateDocuments.php ├── UpdateSchema.php └── UpdateSearchSettings.php ├── Exception └── ApiRateExceededException.php ├── LICENSE ├── NOTICE.txt ├── README.md ├── Tests ├── Integration │ ├── AbstractClientTestCase.php │ ├── AbstractEngineTestCase.php │ ├── AnalyticsApiTest.php │ ├── ClickApiTest.php │ ├── ClientTest.php │ ├── CurationApiTest.php │ ├── DocumentApiTest.php │ ├── EngineApiTest.php │ ├── LogApiTest.php │ ├── QuerySuggestionApiTest.php │ ├── SchemaApiTest.php │ ├── SearchApiTest.php │ ├── SearchSettingsApiTest.php │ ├── SynonymApiTest.php │ └── _data │ │ └── sampleDocs.yml └── Unit │ ├── ClientBuilderTest.php │ └── Connection │ └── Handler │ ├── ApiErrorHandlerTest.php │ ├── RateLimitLoggingHandlerTest.php │ ├── RequestAuthenticationHandlerTest.php │ ├── RequestClientHeaderHandlerTest.php │ └── _data │ └── apiError.yml ├── composer.json ├── docker-compose.yml ├── logo-app-search.png ├── phpcs.xml ├── phpunit.xml.dist ├── resources └── api │ ├── api-spec.yml │ ├── config.json │ └── templates │ ├── README.mustache │ ├── header.mustache │ ├── readme_install.mustache │ ├── readme_title.mustache │ └── readme_usage.mustache ├── ruleset.xml └── scripts └── tests └── run-tests.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | php: 5 | parameters: 6 | php-version: { type: string, default: "7.2" } 7 | docker: 8 | - image: circleci/php:<< parameters.php-version >>-browsers 9 | environment: 10 | CIRCLE_EXECUTOR: php 11 | stack: 12 | parameters: 13 | php-version: { type: string, default: "7.2" } 14 | docker: 15 | - image: circleci/php:<< parameters.php-version >>-browsers 16 | environment: 17 | CIRCLE_EXECUTOR: stack 18 | - image: docker.elastic.co/elasticsearch/elasticsearch:7.13.0 19 | name: elasticsearch 20 | environment: 21 | cluster.name: es-cluster 22 | node.name: es-node 23 | bootstrap.memory_lock: true 24 | discovery.type: single-node 25 | xpack.security.enabled: true 26 | xpack.license.self_generated.type: trial 27 | xpack.security.authc.api_key.enabled: true 28 | ELASTIC_PASSWORD: password 29 | ES_JAVA_OPTS: -Xms512m -Xmx512m 30 | - image: docker.elastic.co/enterprise-search/enterprise-search:7.13.0 31 | name: appsearch 32 | environment: 33 | elasticsearch.host: http://elasticsearch:9200 34 | elasticsearch.password: password 35 | ENT_SEARCH_DEFAULT_PASSWORD: password 36 | secret_management.encryption_keys: "[changeme]" 37 | allow_es_settings_modification: true 38 | elasticsearch.startup_retry.interval: 15 39 | 40 | commands: 41 | install_deps: 42 | steps: 43 | - checkout 44 | - restore_cache: 45 | keys: 46 | - v1-dependencies-{{ checksum "composer.json" }} 47 | - run: 48 | name: Install composer dependencies 49 | command: composer install -n --prefer-dist 50 | - save_cache: 51 | paths: 52 | - ~/.composer/cache/ 53 | key: v1-dependencies-{{ checksum "composer.json" }} 54 | 55 | jobs: 56 | unit-tests: 57 | parameters: 58 | executor: 59 | type: executor 60 | executor: << parameters.executor >> 61 | steps: 62 | - install_deps 63 | - run: 64 | name: Unit testing 65 | command: vendor/bin/phpunit -c phpunit.xml.dist --testsuite unit 66 | integration-tests: 67 | parameters: 68 | executor: 69 | type: executor 70 | executor: << parameters.executor >> 71 | steps: 72 | - install_deps 73 | - run: 74 | name: Configuring App Search 75 | command: | 76 | if [ $CIRCLE_EXECUTOR = "stack" ]; then 77 | export AS_URL="http://appsearch:3002" 78 | export ES_URL="http://elasticsearch:9200" 79 | source .circleci/retrieve-credentials.sh 80 | echo "export AS_URL=$AS_URL" >> $BASH_ENV 81 | echo "export AS_PRIVATE_KEY=$AS_PRIVATE_KEY" >> $BASH_ENV 82 | echo "export AS_SEARCH_KEY=$AS_SEARCH_KEY" >> $BASH_ENV 83 | fi 84 | - run: 85 | name: Integration testing 86 | command: AS_ENGINE_NAME="php-integration-test-$CIRCLE_BUILD_NUM" vendor/bin/phpunit -c phpunit.xml.dist --testsuite integration 87 | qa-phplint: 88 | executor: php 89 | steps: 90 | - install_deps 91 | - run: 92 | name: PHPLint 93 | command: vendor/bin/phplint . --exclude=vendor 94 | qa-phpcs: 95 | executor: php 96 | steps: 97 | - install_deps 98 | - run: 99 | name: PHPCS 100 | command: vendor/bin/phpcs --ignore=vendor,resources . 101 | build-test-image: 102 | docker: 103 | - image: circleci/golang 104 | steps: 105 | - checkout 106 | - setup_remote_docker 107 | - run: 108 | name: Building docker test image 109 | command: docker build . --target test_image -t elastic/app_search_php_client_test:latest 110 | - run: 111 | name: Export docker test image 112 | command: docker save elastic/app_search_php_client_test:latest | gzip -9 > app_search_php_client_test_docker_image.tar.gz 113 | - store_artifacts: 114 | path: ./app_search_php_client_test_docker_image.tar.gz 115 | destination: app_search_php_client_test_docker_image.tar.gz 116 | 117 | workflows: 118 | version: 2 119 | build-and-test: 120 | jobs: 121 | - qa-phplint 122 | - qa-phpcs 123 | - unit-tests: 124 | name: php-73-unit-tests 125 | executor: { name: php, php-version: "7.3" } 126 | requires: 127 | - qa-phplint 128 | - qa-phpcs 129 | - integration-tests: 130 | name: php-73-integration-tests 131 | executor: { name: stack, php-version: "7.3" } 132 | requires: 133 | - php-73-unit-tests 134 | - unit-tests: 135 | name: php-72-unit-tests 136 | executor: { name: php, php-version: "7.2" } 137 | requires: 138 | - qa-phplint 139 | - qa-phpcs 140 | - integration-tests: 141 | name: php-72-integration-tests 142 | executor: { name: stack, php-version: "7.2" } 143 | requires: 144 | - php-72-unit-tests 145 | - unit-tests: 146 | name: php-71-unit-tests 147 | executor: { name: php, php-version: "7.1" } 148 | requires: 149 | - qa-phplint 150 | - qa-phpcs 151 | - integration-tests: 152 | name: php-71-integration-tests 153 | executor: { name: stack, php-version: "7.1" } 154 | requires: 155 | - php-71-unit-tests 156 | - unit-tests: 157 | name: php-70-unit-tests 158 | executor: { name: php, php-version: "7.0" } 159 | requires: 160 | - qa-phplint 161 | - qa-phpcs 162 | - integration-tests: 163 | name: php-70-integration-tests 164 | executor: { name: stack, php-version: "7.0" } 165 | requires: 166 | - php-70-unit-tests 167 | - unit-tests: 168 | name: php-56-unit-tests 169 | executor: { name: php, php-version: "5.6" } 170 | requires: 171 | - qa-phplint 172 | - qa-phpcs 173 | - integration-tests: 174 | name: php-56-integration-tests 175 | executor: { name: stack, php-version: "5.6" } 176 | requires: 177 | - php-56-unit-tests 178 | - build-test-image: 179 | filters: 180 | branches: 181 | only: master 182 | requires: 183 | - php-56-unit-tests 184 | - php-56-integration-tests 185 | - php-70-unit-tests 186 | - php-70-integration-tests 187 | - php-71-unit-tests 188 | - php-71-integration-tests 189 | - php-72-unit-tests 190 | - php-72-integration-tests 191 | - php-73-unit-tests 192 | - php-73-integration-tests 193 | nightly: 194 | triggers: 195 | - schedule: 196 | cron: "0 0 * * *" 197 | filters: 198 | branches: 199 | only: 200 | - master 201 | jobs: 202 | - unit-tests: 203 | executor: { name: php, php-version: "7.3" } 204 | - integration-tests: 205 | executor: { name: stack, php-version: "7.3" } 206 | requires: 207 | - unit-tests 208 | - build-test-image: 209 | requires: 210 | - unit-tests 211 | - integration-tests 212 | -------------------------------------------------------------------------------- /.circleci/retrieve-credentials.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function wait_for_as { 3 | local AS_URL=${AS_URL:-"http://localhost:3002"} 4 | local continue=1 5 | set +e 6 | while [ $continue -gt 0 ]; do 7 | curl --connect-timeout 5 --max-time 10 --retry 10 --retry-delay 0 --retry-max-time 120 --retry-connrefused -s -o /dev/null ${AS_URL}/login 8 | continue=$? 9 | if [ $continue -gt 0 ]; then 10 | sleep 1 11 | fi 12 | done 13 | } 14 | 15 | function load_api_keys { 16 | local AS_USERNAME=${AS_USERNAME:-"enterprise_search"} 17 | local AS_PASSWORD=${AS_PASSWORD:-"password"} 18 | local AS_URL=${AS_URL:-"http://localhost:3002"} 19 | local SEARCH_URL="${AS_URL}/as/credentials/collection?page%5Bcurrent%5D=1" 20 | echo $(curl -u${AS_USERNAME}:${AS_PASSWORD} -s ${SEARCH_URL} | sed -E "s/.*(${1}-[[:alnum:]]{24}).*/\1/") 21 | } 22 | 23 | wait_for_as 24 | export AS_URL=${AS_URL:-"http://localhost:3002"} 25 | export AS_PRIVATE_KEY=`load_api_keys private` 26 | export AS_SEARCH_KEY=`load_api_keys search` 27 | unset -f load_api_keys 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Composer ### 2 | composer.phar 3 | /vendor/ 4 | composer.lock 5 | 6 | ### OpenAPI metadata ### 7 | .openapi-generator/ 8 | .openapi-generator-ignore 9 | 10 | ### IDEs ### 11 | *.tmp 12 | *.bak 13 | *.swp 14 | *~.nib 15 | local.properties 16 | .settings/ 17 | .loadpath 18 | *.launch 19 | .buildpath 20 | .project 21 | .idea/* 22 | out/ 23 | .idea_modules/ 24 | .php_cs.cache 25 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | exclude('vendor') 5 | ->exclude('.circleci') 6 | ->exclude('resources') 7 | ->in(__DIR__) 8 | ; 9 | 10 | return PhpCsFixer\Config::create() 11 | ->setRules([ 12 | '@Symfony' => true, 13 | 'phpdoc_no_package' => false, 14 | 'phpdoc_annotation_without_dot' => false, 15 | 'concat_space' => ['spacing' => 'one'], 16 | ]) 17 | ->setFinder($finder) 18 | ; 19 | -------------------------------------------------------------------------------- /AbstractClient.php: -------------------------------------------------------------------------------- 1 | $engineName, 34 | ]; 35 | 36 | $endpoint = $this->getEndpoint('AddMetaEngineSource'); 37 | $endpoint->setParams($params); 38 | $endpoint->setBody($sourceEngines); 39 | 40 | return $this->performRequest($endpoint); 41 | } 42 | 43 | /** 44 | * Create a new curation. 45 | * 46 | * Documentation: https://swiftype.com/documentation/app-search/api/curations#create 47 | * 48 | * @param string $engineName Name of the engine. 49 | * @param array $queries List of affected search queries. 50 | * @param array $promotedDocIds List of promoted document ids. 51 | * @param array $hiddenDocIds List of hidden document ids. 52 | * 53 | * @return array 54 | */ 55 | public function createCuration($engineName, $queries, $promotedDocIds = null, $hiddenDocIds = null) 56 | { 57 | $params = [ 58 | 'engine_name' => $engineName, 59 | 'queries' => $queries, 60 | 'promoted' => $promotedDocIds, 61 | 'hidden' => $hiddenDocIds, 62 | ]; 63 | 64 | $endpoint = $this->getEndpoint('CreateCuration'); 65 | $endpoint->setParams($params); 66 | 67 | return $this->performRequest($endpoint); 68 | } 69 | 70 | /** 71 | * Create a new synonym set. 72 | * 73 | * Documentation: https://swiftype.com/documentation/app-search/api/synonyms#create 74 | * 75 | * @param string $engineName Name of the engine. 76 | * @param array $synonyms List of synonyms words. 77 | * 78 | * @return array 79 | */ 80 | public function createSynonymSet($engineName, $synonyms) 81 | { 82 | $params = [ 83 | 'engine_name' => $engineName, 84 | 'synonyms' => $synonyms, 85 | ]; 86 | 87 | $endpoint = $this->getEndpoint('CreateSynonymSet'); 88 | $endpoint->setParams($params); 89 | 90 | return $this->performRequest($endpoint); 91 | } 92 | 93 | /** 94 | * Delete a curation by id. 95 | * 96 | * Documentation: https://swiftype.com/documentation/app-search/api/curations#destroy 97 | * 98 | * @param string $engineName Name of the engine. 99 | * @param string $curationId Curation id. 100 | * 101 | * @return array 102 | */ 103 | public function deleteCuration($engineName, $curationId) 104 | { 105 | $params = [ 106 | 'engine_name' => $engineName, 107 | 'curation_id' => $curationId, 108 | ]; 109 | 110 | $endpoint = $this->getEndpoint('DeleteCuration'); 111 | $endpoint->setParams($params); 112 | 113 | return $this->performRequest($endpoint); 114 | } 115 | 116 | /** 117 | * Delete documents by id. 118 | * 119 | * Documentation: https://swiftype.com/documentation/app-search/api/documents#partial 120 | * 121 | * @param string $engineName Name of the engine. 122 | * @param array $documentIds List of document ids. 123 | * 124 | * @return array 125 | */ 126 | public function deleteDocuments($engineName, $documentIds) 127 | { 128 | $params = [ 129 | 'engine_name' => $engineName, 130 | ]; 131 | 132 | $endpoint = $this->getEndpoint('DeleteDocuments'); 133 | $endpoint->setParams($params); 134 | $endpoint->setBody($documentIds); 135 | 136 | return $this->performRequest($endpoint); 137 | } 138 | 139 | /** 140 | * Delete an engine by name. 141 | * 142 | * Documentation: https://swiftype.com/documentation/app-search/api/engines#delete 143 | * 144 | * @param string $engineName Name of the engine. 145 | * 146 | * @return array 147 | */ 148 | public function deleteEngine($engineName) 149 | { 150 | $params = [ 151 | 'engine_name' => $engineName, 152 | ]; 153 | 154 | $endpoint = $this->getEndpoint('DeleteEngine'); 155 | $endpoint->setParams($params); 156 | 157 | return $this->performRequest($endpoint); 158 | } 159 | 160 | /** 161 | * Delete a source engine from a meta engine. 162 | * 163 | * Documentation: https://swiftype.com/documentation/app-search/api/meta-engines#remove-source-engines 164 | * 165 | * @param string $engineName Name of the engine. 166 | * @param array $sourceEngines List of engine ids. 167 | * 168 | * @return array 169 | */ 170 | public function deleteMetaEngineSource($engineName, $sourceEngines) 171 | { 172 | $params = [ 173 | 'engine_name' => $engineName, 174 | ]; 175 | 176 | $endpoint = $this->getEndpoint('DeleteMetaEngineSource'); 177 | $endpoint->setParams($params); 178 | $endpoint->setBody($sourceEngines); 179 | 180 | return $this->performRequest($endpoint); 181 | } 182 | 183 | /** 184 | * Delete a synonym set by id. 185 | * 186 | * Documentation: https://swiftype.com/documentation/app-search/api/synonyms#delete 187 | * 188 | * @param string $engineName Name of the engine. 189 | * @param string $synonymSetId Synonym set id. 190 | * 191 | * @return array 192 | */ 193 | public function deleteSynonymSet($engineName, $synonymSetId) 194 | { 195 | $params = [ 196 | 'engine_name' => $engineName, 197 | 'synonym_set_id' => $synonymSetId, 198 | ]; 199 | 200 | $endpoint = $this->getEndpoint('DeleteSynonymSet'); 201 | $endpoint->setParams($params); 202 | 203 | return $this->performRequest($endpoint); 204 | } 205 | 206 | /** 207 | * Creates a new engine. 208 | * 209 | * Documentation: https://swiftype.com/documentation/app-search/api/engines#create 210 | * 211 | * @param string $name Engine name. 212 | * @param string $language Engine language (null for universal). 213 | * @param string $type Engine type. 214 | * @param array $sourceEngines Sources engines list. 215 | * 216 | * @return array 217 | */ 218 | protected function doCreateEngine($name, $language = null, $type = 'default', $sourceEngines = null) 219 | { 220 | $params = [ 221 | 'name' => $name, 222 | 'language' => $language, 223 | 'type' => $type, 224 | 'source_engines' => $sourceEngines, 225 | ]; 226 | 227 | $endpoint = $this->getEndpoint('DoCreateEngine'); 228 | $endpoint->setParams($params); 229 | 230 | return $this->performRequest($endpoint); 231 | } 232 | 233 | /** 234 | * The API Log displays API request and response data at the Engine level. 235 | * 236 | * Documentation: https://swiftype.com/documentation/app-search/api/logs 237 | * 238 | * @param string $engineName Name of the engine. 239 | * @param string $fromDate Filter date from. 240 | * @param string $toDate Filter date to. 241 | * @param string $currentPage The page to fetch. Defaults to 1. 242 | * @param string $pageSize The number of results per page. 243 | * @param string $query Use this to specify a particular endpoint, like analytics, search, curations and so on. 244 | * @param string $httpStatusFilter Filter based on a particular status code: 400, 401, 403, 429, 200. 245 | * @param string $httpMethodFilter Filter based on a particular HTTP method: GET, POST, PUT, PATCH, DELETE. 246 | * @param string $sortDirection Would you like to have your results ascending, oldest to newest, or descending, newest to oldest? 247 | * 248 | * @return array 249 | */ 250 | public function getApiLogs($engineName, $fromDate, $toDate, $currentPage = null, $pageSize = null, $query = null, $httpStatusFilter = null, $httpMethodFilter = null, $sortDirection = null) 251 | { 252 | $params = [ 253 | 'engine_name' => $engineName, 254 | 'filters.date.from' => $fromDate, 255 | 'filters.date.to' => $toDate, 256 | 'page.current' => $currentPage, 257 | 'page.size' => $pageSize, 258 | 'query' => $query, 259 | 'filters.status' => $httpStatusFilter, 260 | 'filters.method' => $httpMethodFilter, 261 | 'sort_direction' => $sortDirection, 262 | ]; 263 | 264 | $endpoint = $this->getEndpoint('GetApiLogs'); 265 | $endpoint->setParams($params); 266 | 267 | return $this->performRequest($endpoint); 268 | } 269 | 270 | /** 271 | * Returns the number of clicks and total number of queries over a period. 272 | * 273 | * Documentation: https://swiftype.com/documentation/app-search/api/analytics/counts 274 | * 275 | * @param string $engineName Name of the engine. 276 | * @param array $filters Analytics filters 277 | * @param string $interval You can define an interval along with your date range. Can be either hour or day. 278 | * 279 | * @return array 280 | */ 281 | public function getCountAnalytics($engineName, $filters = null, $interval = null) 282 | { 283 | $params = [ 284 | 'engine_name' => $engineName, 285 | 'filters' => $filters, 286 | 'interval' => $interval, 287 | ]; 288 | 289 | $endpoint = $this->getEndpoint('GetCountAnalytics'); 290 | $endpoint->setParams($params); 291 | 292 | return $this->performRequest($endpoint); 293 | } 294 | 295 | /** 296 | * Retrieve a curation by id. 297 | * 298 | * Documentation: https://swiftype.com/documentation/app-search/api/curations#single 299 | * 300 | * @param string $engineName Name of the engine. 301 | * @param string $curationId Curation id. 302 | * 303 | * @return array 304 | */ 305 | public function getCuration($engineName, $curationId) 306 | { 307 | $params = [ 308 | 'engine_name' => $engineName, 309 | 'curation_id' => $curationId, 310 | ]; 311 | 312 | $endpoint = $this->getEndpoint('GetCuration'); 313 | $endpoint->setParams($params); 314 | 315 | return $this->performRequest($endpoint); 316 | } 317 | 318 | /** 319 | * Retrieves one or more documents by id. 320 | * 321 | * Documentation: https://swiftype.com/documentation/app-search/api/documents#get 322 | * 323 | * @param string $engineName Name of the engine. 324 | * @param array $documentIds List of document ids. 325 | * 326 | * @return array 327 | */ 328 | public function getDocuments($engineName, $documentIds) 329 | { 330 | $params = [ 331 | 'engine_name' => $engineName, 332 | ]; 333 | 334 | $endpoint = $this->getEndpoint('GetDocuments'); 335 | $endpoint->setParams($params); 336 | $endpoint->setBody($documentIds); 337 | 338 | return $this->performRequest($endpoint); 339 | } 340 | 341 | /** 342 | * Retrieves an engine by name. 343 | * 344 | * Documentation: https://swiftype.com/documentation/app-search/api/engines#get 345 | * 346 | * @param string $engineName Name of the engine. 347 | * 348 | * @return array 349 | */ 350 | public function getEngine($engineName) 351 | { 352 | $params = [ 353 | 'engine_name' => $engineName, 354 | ]; 355 | 356 | $endpoint = $this->getEndpoint('GetEngine'); 357 | $endpoint->setParams($params); 358 | 359 | return $this->performRequest($endpoint); 360 | } 361 | 362 | /** 363 | * Retrieve current schema for then engine. 364 | * 365 | * Documentation: https://swiftype.com/documentation/app-search/api/schema#read 366 | * 367 | * @param string $engineName Name of the engine. 368 | * 369 | * @return array 370 | */ 371 | public function getSchema($engineName) 372 | { 373 | $params = [ 374 | 'engine_name' => $engineName, 375 | ]; 376 | 377 | $endpoint = $this->getEndpoint('GetSchema'); 378 | $endpoint->setParams($params); 379 | 380 | return $this->performRequest($endpoint); 381 | } 382 | 383 | /** 384 | * Retrive current search settings for the engine. 385 | * 386 | * Documentation: https://swiftype.com/documentation/app-search/api/search-settings#show 387 | * 388 | * @param string $engineName Name of the engine. 389 | * 390 | * @return array 391 | */ 392 | public function getSearchSettings($engineName) 393 | { 394 | $params = [ 395 | 'engine_name' => $engineName, 396 | ]; 397 | 398 | $endpoint = $this->getEndpoint('GetSearchSettings'); 399 | $endpoint->setParams($params); 400 | 401 | return $this->performRequest($endpoint); 402 | } 403 | 404 | /** 405 | * Retrieve a synonym set by id. 406 | * 407 | * Documentation: https://swiftype.com/documentation/app-search/api/synonyms#list-one 408 | * 409 | * @param string $engineName Name of the engine. 410 | * @param string $synonymSetId Synonym set id. 411 | * 412 | * @return array 413 | */ 414 | public function getSynonymSet($engineName, $synonymSetId) 415 | { 416 | $params = [ 417 | 'engine_name' => $engineName, 418 | 'synonym_set_id' => $synonymSetId, 419 | ]; 420 | 421 | $endpoint = $this->getEndpoint('GetSynonymSet'); 422 | $endpoint->setParams($params); 423 | 424 | return $this->performRequest($endpoint); 425 | } 426 | 427 | /** 428 | * Returns the number of clicks received by a document in descending order. 429 | * 430 | * Documentation: https://swiftype.com/documentation/app-search/api/analytics/clicks 431 | * 432 | * @param string $engineName Name of the engine. 433 | * @param string $query Filter clicks over a search query. 434 | * @param string $pageSize The number of results per page. 435 | * @param array $filters Analytics filters 436 | * 437 | * @return array 438 | */ 439 | public function getTopClicksAnalytics($engineName, $query = null, $pageSize = null, $filters = null) 440 | { 441 | $params = [ 442 | 'engine_name' => $engineName, 443 | 'query' => $query, 444 | 'page.size' => $pageSize, 445 | 'filters' => $filters, 446 | ]; 447 | 448 | $endpoint = $this->getEndpoint('GetTopClicksAnalytics'); 449 | $endpoint->setParams($params); 450 | 451 | return $this->performRequest($endpoint); 452 | } 453 | 454 | /** 455 | * Returns queries anlaytics by usage count. 456 | * 457 | * Documentation: https://swiftype.com/documentation/app-search/api/analytics/queries 458 | * 459 | * @param string $engineName Name of the engine. 460 | * @param string $pageSize The number of results per page. 461 | * @param array $filters Analytics filters 462 | * 463 | * @return array 464 | */ 465 | public function getTopQueriesAnalytics($engineName, $pageSize = null, $filters = null) 466 | { 467 | $params = [ 468 | 'engine_name' => $engineName, 469 | 'page.size' => $pageSize, 470 | 'filters' => $filters, 471 | ]; 472 | 473 | $endpoint = $this->getEndpoint('GetTopQueriesAnalytics'); 474 | $endpoint->setParams($params); 475 | 476 | return $this->performRequest($endpoint); 477 | } 478 | 479 | /** 480 | * Create or update documents. 481 | * 482 | * Documentation: https://swiftype.com/documentation/app-search/api/documents#create 483 | * 484 | * @param string $engineName Name of the engine. 485 | * @param array $documents List of document to index. 486 | * 487 | * @return array 488 | */ 489 | public function indexDocuments($engineName, $documents) 490 | { 491 | $params = [ 492 | 'engine_name' => $engineName, 493 | ]; 494 | 495 | $endpoint = $this->getEndpoint('IndexDocuments'); 496 | $endpoint->setParams($params); 497 | $endpoint->setBody($documents); 498 | 499 | return $this->performRequest($endpoint); 500 | } 501 | 502 | /** 503 | * Retrieve available curations for the engine. 504 | * 505 | * Documentation: https://swiftype.com/documentation/app-search/api/curations#read 506 | * 507 | * @param string $engineName Name of the engine. 508 | * @param string $currentPage The page to fetch. Defaults to 1. 509 | * @param string $pageSize The number of results per page. 510 | * 511 | * @return array 512 | */ 513 | public function listCurations($engineName, $currentPage = null, $pageSize = null) 514 | { 515 | $params = [ 516 | 'engine_name' => $engineName, 517 | 'page.current' => $currentPage, 518 | 'page.size' => $pageSize, 519 | ]; 520 | 521 | $endpoint = $this->getEndpoint('ListCurations'); 522 | $endpoint->setParams($params); 523 | 524 | return $this->performRequest($endpoint); 525 | } 526 | 527 | /** 528 | * List all available documents with optional pagination support. 529 | * 530 | * Documentation: https://swiftype.com/documentation/app-search/api/documents#list 531 | * 532 | * @param string $engineName Name of the engine. 533 | * @param string $currentPage The page to fetch. Defaults to 1. 534 | * @param string $pageSize The number of results per page. 535 | * 536 | * @return array 537 | */ 538 | public function listDocuments($engineName, $currentPage = null, $pageSize = null) 539 | { 540 | $params = [ 541 | 'engine_name' => $engineName, 542 | 'page.current' => $currentPage, 543 | 'page.size' => $pageSize, 544 | ]; 545 | 546 | $endpoint = $this->getEndpoint('ListDocuments'); 547 | $endpoint->setParams($params); 548 | 549 | return $this->performRequest($endpoint); 550 | } 551 | 552 | /** 553 | * Retrieves all engines with optional pagination support. 554 | * 555 | * Documentation: https://swiftype.com/documentation/app-search/api/engines#list 556 | * 557 | * @param string $currentPage The page to fetch. Defaults to 1. 558 | * @param string $pageSize The number of results per page. 559 | * 560 | * @return array 561 | */ 562 | public function listEngines($currentPage = null, $pageSize = null) 563 | { 564 | $params = [ 565 | 'page.current' => $currentPage, 566 | 'page.size' => $pageSize, 567 | ]; 568 | 569 | $endpoint = $this->getEndpoint('ListEngines'); 570 | $endpoint->setParams($params); 571 | 572 | return $this->performRequest($endpoint); 573 | } 574 | 575 | /** 576 | * Retrieve available synonym sets for the engine. 577 | * 578 | * Documentation: https://swiftype.com/documentation/app-search/api/synonyms#get 579 | * 580 | * @param string $engineName Name of the engine. 581 | * @param string $currentPage The page to fetch. Defaults to 1. 582 | * @param string $pageSize The number of results per page. 583 | * 584 | * @return array 585 | */ 586 | public function listSynonymSets($engineName, $currentPage = null, $pageSize = null) 587 | { 588 | $params = [ 589 | 'engine_name' => $engineName, 590 | 'page.current' => $currentPage, 591 | 'page.size' => $pageSize, 592 | ]; 593 | 594 | $endpoint = $this->getEndpoint('ListSynonymSets'); 595 | $endpoint->setParams($params); 596 | 597 | return $this->performRequest($endpoint); 598 | } 599 | 600 | /** 601 | * Send data about clicked results. 602 | * 603 | * Documentation: https://swiftype.com/documentation/app-search/api/clickthrough 604 | * 605 | * @param string $engineName Name of the engine. 606 | * @param string $queryText Search query text. 607 | * @param string $documentId The id of the document that was clicked on. 608 | * @param string $requestId The request id returned in the meta tag of a search API response. 609 | * @param array $tags Array of strings representing additional information you wish to track with the clickthrough. 610 | * 611 | * @return array 612 | */ 613 | public function logClickthrough($engineName, $queryText, $documentId, $requestId = null, $tags = null) 614 | { 615 | $params = [ 616 | 'engine_name' => $engineName, 617 | 'query' => $queryText, 618 | 'document_id' => $documentId, 619 | 'request_id' => $requestId, 620 | 'tags' => $tags, 621 | ]; 622 | 623 | $endpoint = $this->getEndpoint('LogClickthrough'); 624 | $endpoint->setParams($params); 625 | 626 | return $this->performRequest($endpoint); 627 | } 628 | 629 | /** 630 | * Run several search in the same request. 631 | * 632 | * Documentation: https://swiftype.com/documentation/app-search/api/search#multi 633 | * 634 | * @param string $engineName Name of the engine. 635 | * @param array $queries Search queries. 636 | * 637 | * @return array 638 | */ 639 | public function multiSearch($engineName, $queries) 640 | { 641 | $params = [ 642 | 'engine_name' => $engineName, 643 | 'queries' => $queries, 644 | ]; 645 | 646 | $endpoint = $this->getEndpoint('MultiSearch'); 647 | $endpoint->setParams($params); 648 | 649 | return $this->performRequest($endpoint); 650 | } 651 | 652 | /** 653 | * Provide relevant query suggestions for incomplete queries. 654 | * 655 | * Documentation: https://swiftype.com/documentation/app-search/api/query-suggestion 656 | * 657 | * @param string $engineName Name of the engine. 658 | * @param string $query A partial query for which to receive suggestions. 659 | * @param array $fields List of fields to use to generate suggestions. Defaults to all text fields. 660 | * @param int $size Number of query suggestions to return. Must be between 1 and 20. Defaults to 5. 661 | * 662 | * @return array 663 | */ 664 | public function querySuggestion($engineName, $query, $fields = null, $size = null) 665 | { 666 | $params = [ 667 | 'engine_name' => $engineName, 668 | 'query' => $query, 669 | 'types.documents.fields' => $fields, 670 | 'size' => $size, 671 | ]; 672 | 673 | $endpoint = $this->getEndpoint('QuerySuggestion'); 674 | $endpoint->setParams($params); 675 | 676 | return $this->performRequest($endpoint); 677 | } 678 | 679 | /** 680 | * Reset search settings for the engine. 681 | * 682 | * Documentation: https://swiftype.com/documentation/app-search/api/search-settings#reset 683 | * 684 | * @param string $engineName Name of the engine. 685 | * 686 | * @return array 687 | */ 688 | public function resetSearchSettings($engineName) 689 | { 690 | $params = [ 691 | 'engine_name' => $engineName, 692 | ]; 693 | 694 | $endpoint = $this->getEndpoint('ResetSearchSettings'); 695 | $endpoint->setParams($params); 696 | 697 | return $this->performRequest($endpoint); 698 | } 699 | 700 | /** 701 | * Allows you to search over, facet and filter your data. 702 | * 703 | * Documentation: https://swiftype.com/documentation/app-search/api/search 704 | * 705 | * @param string $engineName Name of the engine. 706 | * @param string $queryText Search query text. 707 | * @param array $searchRequestParams Search request parameters. 708 | * 709 | * @return array 710 | */ 711 | public function search($engineName, $queryText, $searchRequestParams = null) 712 | { 713 | $params = [ 714 | 'engine_name' => $engineName, 715 | 'query' => $queryText, 716 | ]; 717 | 718 | $endpoint = $this->getEndpoint('Search'); 719 | $endpoint->setParams($params); 720 | $endpoint->setBody($searchRequestParams); 721 | 722 | return $this->performRequest($endpoint); 723 | } 724 | 725 | /** 726 | * Update an existing curation. 727 | * 728 | * Documentation: https://swiftype.com/documentation/app-search/api/curations#update 729 | * 730 | * @param string $engineName Name of the engine. 731 | * @param string $curationId Curation id. 732 | * @param array $queries List of affected search queries. 733 | * @param array $promotedDocIds List of promoted document ids. 734 | * @param array $hiddenDocIds List of hidden document ids. 735 | * 736 | * @return array 737 | */ 738 | public function updateCuration($engineName, $curationId, $queries, $promotedDocIds = null, $hiddenDocIds = null) 739 | { 740 | $params = [ 741 | 'engine_name' => $engineName, 742 | 'curation_id' => $curationId, 743 | 'queries' => $queries, 744 | 'promoted' => $promotedDocIds, 745 | 'hidden' => $hiddenDocIds, 746 | ]; 747 | 748 | $endpoint = $this->getEndpoint('UpdateCuration'); 749 | $endpoint->setParams($params); 750 | 751 | return $this->performRequest($endpoint); 752 | } 753 | 754 | /** 755 | * Partial update of documents. 756 | * 757 | * Documentation: https://swiftype.com/documentation/app-search/api/documents#partial 758 | * 759 | * @param string $engineName Name of the engine. 760 | * @param array $documents List of document to update. 761 | * 762 | * @return array 763 | */ 764 | public function updateDocuments($engineName, $documents) 765 | { 766 | $params = [ 767 | 'engine_name' => $engineName, 768 | ]; 769 | 770 | $endpoint = $this->getEndpoint('UpdateDocuments'); 771 | $endpoint->setParams($params); 772 | $endpoint->setBody($documents); 773 | 774 | return $this->performRequest($endpoint); 775 | } 776 | 777 | /** 778 | * Update schema for the current engine. 779 | * 780 | * Documentation: https://swiftype.com/documentation/app-search/api/schema#patch 781 | * 782 | * @param string $engineName Name of the engine. 783 | * @param array $schema Schema description. 784 | * 785 | * @return array 786 | */ 787 | public function updateSchema($engineName, $schema) 788 | { 789 | $params = [ 790 | 'engine_name' => $engineName, 791 | ]; 792 | 793 | $endpoint = $this->getEndpoint('UpdateSchema'); 794 | $endpoint->setParams($params); 795 | $endpoint->setBody($schema); 796 | 797 | return $this->performRequest($endpoint); 798 | } 799 | 800 | /** 801 | * Update search settings for the engine. 802 | * 803 | * Documentation: https://swiftype.com/documentation/app-search/api/search-settings#update 804 | * 805 | * @param string $engineName Name of the engine. 806 | * @param array $searchSettings Search settings. 807 | * 808 | * @return array 809 | */ 810 | public function updateSearchSettings($engineName, $searchSettings) 811 | { 812 | $params = [ 813 | 'engine_name' => $engineName, 814 | ]; 815 | 816 | $endpoint = $this->getEndpoint('UpdateSearchSettings'); 817 | $endpoint->setParams($params); 818 | $endpoint->setBody($searchSettings); 819 | 820 | return $this->performRequest($endpoint); 821 | } 822 | 823 | // phpcs:enable 824 | } 825 | -------------------------------------------------------------------------------- /Client.php: -------------------------------------------------------------------------------- 1 | doCreateEngine($name, $language); 31 | } 32 | 33 | /** 34 | * Creates a new meta engine. 35 | * 36 | * Documentation: https://swiftype.com/documentation/app-search/api/engines#create 37 | * 38 | * @param string $name Engine name. 39 | * @param string[] $sourceEngines Source engines list. 40 | * 41 | * @return array 42 | */ 43 | public function createMetaEngine($name, array $sourceEngines) 44 | { 45 | return $this->doCreateEngine($name, null, 'meta', $sourceEngines); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ClientBuilder.php: -------------------------------------------------------------------------------- 1 | setHost($apiEndpoint)->setApiKey($apiKey); 45 | } 46 | 47 | /** 48 | * Set the api key for the client. 49 | * 50 | * @param string $apiKey 51 | * 52 | * @return ClientBuilder 53 | */ 54 | public function setApiKey($apiKey) 55 | { 56 | $this->apiKey = $apiKey; 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * Set integration name & version for the client. 63 | * 64 | * @param string $integration 65 | * 66 | * @return ClientBuilder 67 | */ 68 | public function setIntegration($integration) 69 | { 70 | $this->integration = $integration; 71 | 72 | return $this; 73 | } 74 | 75 | /** 76 | * Set the api endpoint for the client. 77 | * 78 | * @param string $host 79 | * 80 | * @return ClientBuilder 81 | */ 82 | public function setHost($host) 83 | { 84 | $isValidEndpoint = false; 85 | $testedEndpoint = $host; 86 | 87 | if (filter_var($testedEndpoint, FILTER_VALIDATE_URL)) { 88 | $isValidEndpoint = true; 89 | } 90 | 91 | if (!$isValidEndpoint) { 92 | $testedEndpoint = sprintf('https://%s', $testedEndpoint); 93 | $isValidEndpoint = false != filter_var($testedEndpoint, FILTER_VALIDATE_URL); 94 | } 95 | 96 | if (!$isValidEndpoint) { 97 | $testedEndpoint = sprintf('%s.%s', $testedEndpoint, 'api.swiftype.com'); 98 | $isValidEndpoint = false != filter_var($testedEndpoint, FILTER_VALIDATE_URL); 99 | } 100 | 101 | if (!$isValidEndpoint) { 102 | throw new \Elastic\OpenApi\Codegen\Exception\UnexpectedValueException("Invalid API endpoint : $host"); 103 | } 104 | 105 | return parent::setHost($testedEndpoint); 106 | } 107 | 108 | /** 109 | * Return the configured App Search client. 110 | * 111 | * @return \Elastic\AppSearch\Client\Client 112 | */ 113 | public function build() 114 | { 115 | return new Client($this->getEndpointBuilder(), $this->getConnection()); 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | protected function getHandler() 122 | { 123 | $handler = parent::getHandler(); 124 | 125 | $handler = new Connection\Handler\RequestClientHeaderHandler($handler, $this->integration); 126 | $handler = new Connection\Handler\RequestAuthenticationHandler($handler, $this->apiKey); 127 | $handler = new \Elastic\OpenApi\Codegen\Connection\Handler\RequestUrlPrefixHandler($handler, self::URI_PREFIX); 128 | $handler = new Connection\Handler\ApiErrorHandler($handler); 129 | $handler = new Connection\Handler\RateLimitLoggingHandler($handler, $this->getLogger()); 130 | 131 | return $handler; 132 | } 133 | 134 | /** 135 | * {@inheritdoc} 136 | */ 137 | protected function getEndpointBuilder() 138 | { 139 | return new \Elastic\OpenApi\Codegen\Endpoint\Builder(__NAMESPACE__ . "\Endpoint"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Connection/Handler/ApiErrorHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 39 | } 40 | 41 | /** 42 | * Proxy the response and throw an exception if a http error is detected. 43 | * 44 | * @param array $request request 45 | * 46 | * @return array 47 | */ 48 | public function __invoke($request) 49 | { 50 | $handler = $this->handler; 51 | $response = Core::proxy($handler($request), function ($response) use ($request) { 52 | if ($response['status'] >= 400) { 53 | $exception = new ApiException($this->getErrorMessage($response)); 54 | switch ($response['status']) { 55 | case 401: 56 | case 403: 57 | $exception = new AuthenticationException($exception->getMessage()); 58 | break; 59 | case 404: 60 | $exception = new NotFoundException($exception->getMessage()); 61 | break; 62 | case 429: 63 | $exception = $this->getApiRateExceededException($exception->getMessage(), $response); 64 | break; 65 | case 400: 66 | $exception = new BadRequestException($exception->getMessage()); 67 | break; 68 | } 69 | 70 | throw $exception; 71 | } 72 | 73 | return $response; 74 | }); 75 | 76 | return $response; 77 | } 78 | 79 | /** 80 | * Process the error message from the response body. 81 | * 82 | * @param array $response 83 | * 84 | * @return string 85 | */ 86 | private function getErrorMessage($response) 87 | { 88 | $message = isset($response['reason']) ? $response['reason'] : 'Unexpected error.'; 89 | 90 | if (!empty($response['body']['errors'])) { 91 | $message = $response['body']['errors']; 92 | } elseif (!empty($response['body']['error'])) { 93 | $message = $response['body']['error']; 94 | } 95 | 96 | return is_array($message) ? implode(' ', $message) : $message; 97 | } 98 | 99 | /** 100 | * Build an ApiRateExceededException from the response. 101 | * 102 | * @param string $message 103 | * @param array $response 104 | * 105 | * @return \Elastic\AppSearch\Client\Exception\ApiRateExceededException 106 | */ 107 | private function getApiRateExceededException($message, $response) 108 | { 109 | $limit = null; 110 | $retryAfter = null; 111 | 112 | if (Core::hasHeader($response, RateLimitLoggingHandler::RATE_LIMIT_LIMIT_HEADER_NAME)) { 113 | $limit = Core::firstHeader($response, RateLimitLoggingHandler::RATE_LIMIT_LIMIT_HEADER_NAME); 114 | } 115 | 116 | if (Core::hasHeader($response, RateLimitLoggingHandler::RETRY_AFTER_HEADER_NAME)) { 117 | $retryAfter = Core::firstHeader($response, RateLimitLoggingHandler::RETRY_AFTER_HEADER_NAME); 118 | } 119 | 120 | return new ApiRateExceededException($message, $limit, $retryAfter); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Connection/Handler/RateLimitLoggingHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 60 | $this->logger = $logger; 61 | } 62 | 63 | /** 64 | * Proxy the response and throw an exception if a http error is detected. 65 | * 66 | * @param array $request request 67 | * 68 | * @return array 69 | */ 70 | public function __invoke($request) 71 | { 72 | $handler = $this->handler; 73 | $response = Core::proxy($handler($request), function ($response) { 74 | if ($this->isRateLimitWarning($response)) { 75 | $message = sprintf( 76 | 'AppSearch Rate Limit: %s remaining of %s allowed', 77 | $this->getRemainingRateLimit($response), 78 | $this->getAllowedRateLimit($response) 79 | ); 80 | $this->logger->warning($message); 81 | } 82 | 83 | return $response; 84 | }); 85 | 86 | return $response; 87 | } 88 | 89 | /** 90 | * Indicate if a warning should be logged or not. 91 | * 92 | * @param array $response 93 | * 94 | * @return bool 95 | */ 96 | private function isRateLimitWarning($response) 97 | { 98 | $allowedRateLimit = $this->getAllowedRateLimit($response); 99 | $remainingRateLimit = $this->getRemainingRateLimit($response); 100 | 101 | if (null === $allowedRateLimit || null === $remainingRateLimit) { 102 | return false; 103 | } 104 | 105 | return ($remainingRateLimit / $allowedRateLimit) < self::RATE_LIMIT_PERCENT_WARNING_TRESHOLD; 106 | } 107 | 108 | /** 109 | * Read the allowed rate limit from response header. 110 | * 111 | * @param array $response 112 | * 113 | * @return null|int 114 | */ 115 | private function getAllowedRateLimit($response) 116 | { 117 | return $this->getHeaderValue($response, self::RATE_LIMIT_LIMIT_HEADER_NAME); 118 | } 119 | 120 | /** 121 | * Read the remaining rate limit from response header. 122 | * 123 | * @param array $response 124 | * 125 | * @return null|int 126 | */ 127 | private function getRemainingRateLimit($response) 128 | { 129 | return $this->getHeaderValue($response, self::RATE_LIMIT_REMAINING_HEADER_NAME); 130 | } 131 | 132 | /** 133 | * Read an unique value from response headers. 134 | * 135 | * @param array $response 136 | * 137 | * @return null|int 138 | */ 139 | private function getHeaderValue($response, $headerName) 140 | { 141 | return Core::firstHeader($response, $headerName); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Connection/Handler/RequestAuthenticationHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 50 | $this->apiKey = $apiKey; 51 | } 52 | 53 | /** 54 | * Add API key before calling the original handler. 55 | * 56 | * @param array $request original request 57 | * 58 | * @return array 59 | */ 60 | public function __invoke($request) 61 | { 62 | $handler = $this->handler; 63 | 64 | $headerValue = [sprintf(self::HEADER_VALUE_PATTERN, $this->apiKey)]; 65 | $request = Core::setHeader($request, self::HEADER_NAME, $headerValue); 66 | 67 | return $handler($request); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Connection/Handler/RequestClientHeaderHandler.php: -------------------------------------------------------------------------------- 1 | handler = $handler; 70 | $this->integration = $integration; 71 | } 72 | 73 | /** 74 | * Add reporting headers before calling the original handler. 75 | * 76 | * @param array $request original request 77 | * 78 | * @return array 79 | */ 80 | public function __invoke($request) 81 | { 82 | $handler = $this->handler; 83 | 84 | $request = Core::setHeader($request, self::CLIENT_NAME_HEADER, [self::CLIENT_NAME_VALUE]); 85 | $request = Core::setHeader($request, self::CLIENT_VERSION_HEADER, [self::CLIENT_VERSION_VALUE]); 86 | 87 | if (null !== $this->integration) { 88 | list($integrationName, $integrationVersion) = explode(':', $this->integration); 89 | if ($integrationName) { 90 | $request = Core::setHeader($request, self::CLIENT_INTEGRATION_NAME_HEADER, [$integrationName]); 91 | } 92 | if ($integrationVersion) { 93 | $request = Core::setHeader($request, self::CLIENT_INTEGRATION_VERSION_HEADER, [$integrationVersion]); 94 | } 95 | } 96 | 97 | return $handler($request); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG base_image=php:7.2-cli 2 | FROM $base_image as builder 3 | 4 | # Installing additional tools 5 | RUN apt-get update \ 6 | && apt-get install -y git unzip \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | # Installing composer as a globally available system command. 10 | RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ 11 | && php composer-setup.php \ 12 | && php -r "unlink('composer-setup.php');" \ 13 | && mv composer.phar /usr/local/bin/composer 14 | 15 | 16 | FROM builder as test_image 17 | 18 | WORKDIR /app 19 | COPY . /app/ 20 | RUN composer install 21 | -------------------------------------------------------------------------------- /Endpoint/AddMetaEngineSource.php: -------------------------------------------------------------------------------- 1 | limit = $limit; 46 | $this->retryAfter = $retryAfter; 47 | } 48 | 49 | /** 50 | * Return the max number of call allowed over a period. 51 | * 52 | * @return string 53 | */ 54 | public function getApiRateLimit() 55 | { 56 | return $this->limit; 57 | } 58 | 59 | /** 60 | * Indicate the ttl before trying again to use the API. 61 | * 62 | * @return string 63 | */ 64 | public function getRetryAfter() 65 | { 66 | return $this->retryAfter; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Elasticsearch BV 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Elastic App Search PHP client. 2 | 3 | Copyright 2012-2019 Elasticsearch B.V. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **⚠️ This client is deprecated ⚠️** 2 | > 3 | > As of Enterprise Search version 7.13.0, we are directing users to the new [Enterprise Search PHP Client](https://github.com/elastic/enterprise-search-php) and 4 | > deprecating this client. 5 | > 6 | > This client will be compatible with all Enterprise Search 7.x releases, but will not be compatible with 8.x releases. Our development effort on this project will 7 | > be limited to bug fixes. All future enhancements will be focused on the Enterprise Search PHP Client. 8 | > 9 | > Thank you! - Elastic 10 | 11 | 12 |

Elastic App Search Logo

13 | 14 |

CircleCI buidl

15 | 16 | > A first-party PHP client for building excellent, relevant search experiences with [Elastic App Search](https://www.elastic.co/products/app-search). 17 | 18 | ## Contents 19 | 20 | - [Getting started](#getting-started-) 21 | - [Usage](#usage) 22 | - [Development](#development) 23 | - [FAQ](#faq-) 24 | - [Contribute](#contribute-) 25 | - [License](#license-) 26 | 27 | *** 28 | 29 | ## Getting started 🐣 30 | 31 | Using this client assumes that you have already an instance of Elastic App Search up and running. 32 | 33 | 34 | 35 | You can find more information about Elastic App Search at : https://www.elastic.co/app-search. 36 | 37 | You can install the client in your project by using composer: 38 | 39 | ```bash 40 | composer require elastic/app-search 41 | ``` 42 | 43 | ### Versioning 44 | 45 | This client is versioned and released alongside App Search. 46 | 47 | To guarantee compatibility, use the most recent version of this library within the major version of the corresponding App Search implementation. 48 | 49 | For example, for App Search `7.3`, use `7.3` of this library or above, but not `8.0`. 50 | 51 | If you are using the [SaaS version available on swiftype.com](https://app.swiftype.com/as) of App Search, you should use the version 7.5.x of the client. 52 | 53 | ## Usage 54 | 55 | ### Configuring the client 56 | 57 | #### Basic client instantiation 58 | 59 | To instantiate a new client you can use `\Elastic\AppSearch\Client\ClientBuilder`: 60 | 61 | ```php 62 | $apiEndpoint = 'http://localhost:3002/'; 63 | $apiKey = 'private-XXXXXXXXXXXX'; 64 | $clientBuilder = \Elastic\AppSearch\Client\ClientBuilder::create($apiEndpoint, $apiKey); 65 | 66 | $client = $clientBuilder->build(); 67 | ``` 68 | 69 | **Notes:** 70 | 71 | - The resulting client will be of type `\Elastic\AppSearch\Client\Client` 72 | 73 | - You can find the API endpoint and your API key URL in the credentials sections of the App Search dashboard. 74 | 75 | - You can use any type of API Key (private, public or admin). The client will throw an exception if you try to execute an action that is not authorized for the key used. 76 | 77 | ### Basic usage 78 | 79 | #### Retrieve or create an engine 80 | 81 | Most methods of the API require that you have access to an Engine. 82 | 83 | To check if an Engine exists and retrieve its configuration, you can use the `Client::getEngine` method : 84 | 85 | ```php 86 | $engine = $client->getEngine('my-engine'); 87 | ``` 88 | 89 | If the Engine does not exists yet, you can create it by using the `Client::createEngine` method : 90 | 91 | ```php 92 | $engine = $client->createEngine('my-engine', 'en'); 93 | ``` 94 | 95 | The second parameter (`$language`) is optional. Set it to `null` to apply the `universal` language. 96 | 97 | [Read more](https://swiftype.com/documentation/app-search/api/engines#multi-language) about language support. 98 | 99 | #### Index some documents 100 | 101 | You can use the `Client::indexDocuments` method to index some documents into the Engine: 102 | 103 | ```php 104 | $documents = [ 105 | ['id' => 'first-document', 'name' => 'Document name', 'description' => 'Document description'], 106 | ['id' => 'other-document', 'name' => 'Other document name', 'description' => 'Other description'], 107 | ]; 108 | 109 | $indexingResults = $client->indexDocuments('my-engine', $documents); 110 | ``` 111 | 112 | The `$indexingResults` array will contain the result of the indexation of each documents. You should always check the content of the result. 113 | 114 | [Read more](https://swiftype.com/documentation/app-search/api/documents#create) about document indexing. 115 | 116 | #### Search 117 | 118 | You can use the `Client::search` method to search in your Engine: 119 | 120 | ```php 121 | $searchParams = [ 122 | 'page' => ['current' => 1, 'size' => 10] 123 | ]; 124 | 125 | $searchResponse = $client->search('my-engine', 'search text', $searchParams); 126 | ``` 127 | If you want to match all documents you can use and empty search query `''` as second parameter (`$queryText`). 128 | 129 | The `$searchRequestParams` parameter is optional and can be used to use advanced search features. Allowed params are : 130 | 131 | Param name | Documentation URL 132 | --------------- | ---------------------------------------------------------------------- 133 | `page` | https://swiftype.com/documentation/app-search/api/search#paging 134 | `filters` | https://swiftype.com/documentation/app-search/api/search/filters 135 | `facets` | https://swiftype.com/documentation/app-search/api/search/facets 136 | `sort` | https://swiftype.com/documentation/app-search/api/search/sorting 137 | `boosts` | https://swiftype.com/documentation/app-search/api/search/boosts 138 | `search_fields` | https://swiftype.com/documentation/app-search/api/search/search-fields 139 | `result_fields` | https://swiftype.com/documentation/app-search/api/search/result-fields 140 | `group` | https://swiftype.com/documentation/app-search/api/search/grouping 141 | 142 | The search response will contains at least a meta field and a results field as shown in this example: 143 | 144 | ```php 145 | [ 146 | 'meta' => [ 147 | 'warnings' => [], 148 | 'page' => [ 149 | 'current' => 1, 150 | 'total_pages' => 1, 151 | 'total_results' => 1, 152 | 'size' => 10 153 | ], 154 | 'request_id' => 'feff7cf2359a6f6da84586969ef0ca89' 155 | ], 156 | 'results' => [ 157 | [ 158 | 'id' => ['raw' => 'first-document'], 159 | 'name' => ['raw' => 'Document name'], 160 | 'description' => ['raw' => ['Document description'] 161 | ] 162 | ] 163 | ] 164 | ] 165 | ``` 166 | 167 | ### Clients methods 168 | 169 | Method | Description | Documentation 170 | ------------|-------------|-------------- 171 | **`createEngine`**| Creates a new engine.

**Parameters :**
- `$name` (required)
- `$language`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#create) 172 | **`createMetaEngine`**| Creates a new meta engine.

**Parameters :**
- `$name` (required)
- `$sourceEngines` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#create) 173 | **`addMetaEngineSource`**| Add a source engine to an existing meta engine.

**Parameters :**
- `$engineName` (required)
- `$sourceEngines` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/meta-engines#add-source-engines) 174 | **`createCuration`**| Create a new curation.

**Parameters :**
- `$engineName` (required)
- `$queries` (required)
- `$promotedDocIds`
- `$hiddenDocIds`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/curations#create) 175 | **`createSynonymSet`**| Create a new synonym set.

**Parameters :**
- `$engineName` (required)
- `$synonyms` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/synonyms#create) 176 | **`deleteCuration`**| Delete a curation by id.

**Parameters :**
- `$engineName` (required)
- `$curationId` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/curations#destroy) 177 | **`deleteDocuments`**| Delete documents by id.

**Parameters :**
- `$engineName` (required)
- `$documentIds` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/documents#partial) 178 | **`deleteEngine`**| Delete an engine by name.

**Parameters :**
- `$engineName` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#delete) 179 | **`deleteMetaEngineSource`**| Delete a source engine from a meta engine.

**Parameters :**
- `$engineName` (required)
- `$sourceEngines` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/meta-engines#remove-source-engines) 180 | **`deleteSynonymSet`**| Delete a synonym set by id.

**Parameters :**
- `$engineName` (required)
- `$synonymSetId` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/synonyms#delete) 181 | **`getApiLogs`**| The API Log displays API request and response data at the Engine level.

**Parameters :**
- `$engineName` (required)
- `$fromDate` (required)
- `$toDate` (required)
- `$currentPage`
- `$pageSize`
- `$query`
- `$httpStatusFilter`
- `$httpMethodFilter`
- `$sortDirection`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/logs) 182 | **`getCountAnalytics`**| Returns the number of clicks and total number of queries over a period.

**Parameters :**
- `$engineName` (required)
- `$filters`
- `$interval`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/analytics/counts) 183 | **`getCuration`**| Retrieve a curation by id.

**Parameters :**
- `$engineName` (required)
- `$curationId` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/curations#single) 184 | **`getDocuments`**| Retrieves one or more documents by id.

**Parameters :**
- `$engineName` (required)
- `$documentIds` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/documents#get) 185 | **`getEngine`**| Retrieves an engine by name.

**Parameters :**
- `$engineName` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#get) 186 | **`getSchema`**| Retrieve current schema for then engine.

**Parameters :**
- `$engineName` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/schema#read) 187 | **`getSearchSettings`**| Retrive current search settings for the engine.

**Parameters :**
- `$engineName` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/search-settings#show) 188 | **`getSynonymSet`**| Retrieve a synonym set by id.

**Parameters :**
- `$engineName` (required)
- `$synonymSetId` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/synonyms#list-one) 189 | **`getTopClicksAnalytics`**| Returns the number of clicks received by a document in descending order.

**Parameters :**
- `$engineName` (required)
- `$query`
- `$pageSize`
- `$filters`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/analytics/clicks) 190 | **`getTopQueriesAnalytics`**| Returns queries anlaytics by usage count.

**Parameters :**
- `$engineName` (required)
- `$pageSize`
- `$filters`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/analytics/queries) 191 | **`indexDocuments`**| Create or update documents.

**Parameters :**
- `$engineName` (required)
- `$documents` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/documents#create) 192 | **`listCurations`**| Retrieve available curations for the engine.

**Parameters :**
- `$engineName` (required)
- `$currentPage`
- `$pageSize`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/curations#read) 193 | **`listDocuments`**| List all available documents with optional pagination support.

**Parameters :**
- `$engineName` (required)
- `$currentPage`
- `$pageSize`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/documents#list) 194 | **`listEngines`**| Retrieves all engines with optional pagination support.

**Parameters :**
- `$currentPage`
- `$pageSize`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#list) 195 | **`listSynonymSets`**| Retrieve available synonym sets for the engine.

**Parameters :**
- `$engineName` (required)
- `$currentPage`
- `$pageSize`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/synonyms#get) 196 | **`logClickthrough`**| Send data about clicked results.

**Parameters :**
- `$engineName` (required)
- `$queryText` (required)
- `$documentId` (required)
- `$requestId`
- `$tags`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/clickthrough) 197 | **`multiSearch`**| Run several search in the same request.

**Parameters :**
- `$engineName` (required)
- `$queries` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/search#multi) 198 | **`querySuggestion`**| Provide relevant query suggestions for incomplete queries.

**Parameters :**
- `$engineName` (required)
- `$query` (required)
- `$fields`
- `$size`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/query-suggestion) 199 | **`resetSearchSettings`**| Reset search settings for the engine.

**Parameters :**
- `$engineName` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/search-settings#reset) 200 | **`search`**| Allows you to search over, facet and filter your data.

**Parameters :**
- `$engineName` (required)
- `$queryText` (required)
- `$searchRequestParams`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/search) 201 | **`updateCuration`**| Update an existing curation.

**Parameters :**
- `$engineName` (required)
- `$curationId` (required)
- `$queries` (required)
- `$promotedDocIds`
- `$hiddenDocIds`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/curations#update) 202 | **`updateDocuments`**| Partial update of documents.

**Parameters :**
- `$engineName` (required)
- `$documents` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/documents#partial) 203 | **`updateSchema`**| Update schema for the current engine.

**Parameters :**
- `$engineName` (required)
- `$schema` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/schema#patch) 204 | **`updateSearchSettings`**| Update search settings for the engine.

**Parameters :**
- `$engineName` (required)
- `$searchSettings` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/search-settings#update) 205 | 206 | ## Development 207 | 208 | Code for the endpoints is generated automatically using a custom version of [OpenAPI Generator](https://github.com/openapitools/openapi-generator). 209 | 210 | To regenerate endpoints, use the docker laucher packaged in `vendor/bin`: 211 | 212 | ```bash 213 | ./vendor/bin/elastic-openapi-codegen.sh 214 | ``` 215 | 216 | The custom generator will be built and launched using the following Open API spec file : `resources/api/api-spec.yml`. 217 | 218 | You can then commit and PR the modified api-spec file and your endpoints code files. 219 | 220 | The client class and readme may be changed in some cases. Do not forget to include them in your commit! 221 | 222 | ## FAQ 🔮 223 | 224 | ### Where do I report issues with the client? 225 | 226 | If something is not working as expected, please open an [issue](https://github.com/elastic/app-search-php/issues/new). 227 | 228 | ### Where can I find the full API documentation ? 229 | 230 | Your best bet is to read the [documentation](https://swiftype.com/documentation/app-search). 231 | 232 | ### Where else can I go to get help? 233 | 234 | You can checkout the [Elastic community discuss forums](https://discuss.elastic.co/c/app-search). 235 | 236 | ## Contribute 🚀 237 | 238 | We welcome contributors to the project. Before you begin, a couple notes... 239 | 240 | + Before opening a pull request, please create an issue to [discuss the scope of your proposal](https://github.com/elastic/app-search-php/issues). 241 | + Please write simple code and concise documentation, when appropriate. 242 | 243 | ## License 📗 244 | 245 | [Apache 2.0](https://github.com/elastic/app-search-php/blob/master/LICENSE) © [Elastic](https://github.com/elastic) 246 | 247 | Thank you to all the [contributors](https://github.com/elastic/app-search-php/graphs/contributors)! 248 | 249 | -------------------------------------------------------------------------------- /Tests/Integration/AbstractClientTestCase.php: -------------------------------------------------------------------------------- 1 | create(getenv('AS_URL'), getenv('AS_PRIVATE_KEY'))->build(); 34 | } 35 | 36 | /** 37 | * @return \Elastic\AppSearch\Client\Client 38 | */ 39 | protected static function getDefaultClient() 40 | { 41 | return self::$defaultClient; 42 | } 43 | 44 | /** 45 | * @return string 46 | */ 47 | protected static function getDefaultEngineName() 48 | { 49 | $enginePrefix = getenv('AS_ENGINE_NAME') ? getenv('AS_ENGINE_NAME') : 'php-integration-test'; 50 | $className = explode('\\', get_called_class()); 51 | $engineSuffix = strtolower(end($className)); 52 | 53 | return str_replace('.', '-', sprintf('%s-%s', $enginePrefix, $engineSuffix)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/Integration/AbstractEngineTestCase.php: -------------------------------------------------------------------------------- 1 | getEngine(self::getDefaultEngineName()); 49 | $hasEngine = true; 50 | } catch (NotFoundException $e) { 51 | $hasEngine = false; 52 | } 53 | if ($hasEngine && $tryDelete) { 54 | self::tearDownAfterClass(); 55 | $tryDelete = false; 56 | } 57 | } while ($hasEngine); 58 | 59 | self::getDefaultClient()->createEngine(self::getDefaultEngineName()); 60 | 61 | if (static::$importSampleDocs) { 62 | self::importSampleDocuments(); 63 | } 64 | } 65 | 66 | /** 67 | * Delete the default engine before exiting the class. 68 | */ 69 | public static function tearDownAfterClass() 70 | { 71 | self::getDefaultClient()->deleteEngine(self::getDefaultEngineName()); 72 | } 73 | 74 | /** 75 | * Import sample data into the default engine. 76 | */ 77 | protected static function importSampleDocuments($waitForSearchableDocs = true) 78 | { 79 | $client = self::getDefaultClient(); 80 | $engineName = self::getDefaultEngineName(); 81 | $documents = self::getSampleDocuments(); 82 | 83 | $indexingResponse = $client->indexDocuments($engineName, $documents); 84 | 85 | if ($waitForSearchableDocs) { 86 | $isReady = false; 87 | 88 | while (false === $isReady) { 89 | usleep(self::SYNC_RETRY_INTERVAL); 90 | 91 | // We also wait for the schema to be synced. 92 | $schema = $client->getSchema($engineName); 93 | $isSchemaSynced = !empty($schema); 94 | 95 | if ($isSchemaSynced) { 96 | // We wait for the docs to be searchable before launching the test. 97 | $searchResponse = $client->search($engineName, ''); 98 | $areDocsSynced = $searchResponse['meta']['page']['total_results'] == count($documents); 99 | 100 | $isReady = $isSchemaSynced && $areDocsSynced; 101 | } 102 | } 103 | } 104 | 105 | return $indexingResponse; 106 | } 107 | 108 | protected static function getSampleDocuments() 109 | { 110 | $parser = new \Symfony\Component\Yaml\Parser(); 111 | 112 | return $parser->parseFile(self::DOC_SAMPLE_FILE); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Tests/Integration/AnalyticsApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 28 | $engine = $this->getDefaultEngineName(); 29 | 30 | $topClicks = $client->getTopClicksAnalytics($engine, $searchTerm, $size); 31 | 32 | $this->assertLessThanOrEqual($size, $topClicks['meta']['page']['size']); 33 | $this->assertArrayHasKey('results', $topClicks); 34 | } 35 | 36 | /** 37 | * Test top queries analytics request. 38 | * 39 | * @testWith [3, null, null] 40 | * [3, true, true] 41 | * [3, true, false] 42 | * [20, false, true] 43 | * [20, false, false] 44 | */ 45 | public function testTopQueries($size, $withResults, $clicked) 46 | { 47 | $client = $this->getDefaultClient(); 48 | $engine = $this->getDefaultEngineName(); 49 | 50 | $filters = []; 51 | 52 | if (null != $withResults) { 53 | $filters['all'][] = ['results' => $withResults]; 54 | } 55 | 56 | if (null != $clicked) { 57 | $filters['all'][] = ['clicks' => $clicked]; 58 | } 59 | 60 | $queries = $client->getTopQueriesAnalytics($engine, $size, !empty($filters) ? $filters : null); 61 | 62 | $this->assertLessThanOrEqual($size, $queries['meta']['page']['size']); 63 | $this->assertArrayHasKey('results', $queries); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/Integration/ClickApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 25 | $engineName = $this->getDefaultEngineName(); 26 | 27 | $this->assertEmpty($client->logClickthrough($engineName, 'cat', 'INscMGmhmX4')); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/Integration/ClientTest.php: -------------------------------------------------------------------------------- 1 | build(); 28 | $this->assertInstanceOf(Client::class, $client); 29 | } 30 | 31 | /** 32 | * Test exception throwned when providing an invalid API endpoint. 33 | * 34 | * @param string API Endpoint. 35 | * @param string Class of the exception that should be raised. 36 | * 37 | * @testWith ["http://localhost:5000", "\\Elastic\\OpenApi\\Codegen\\Exception\\CouldNotConnectToHostException"] 38 | * ["http://foo.bar:5000", "\\Elastic\\OpenApi\\Codegen\\Exception\\CouldNotResolveHostException"] 39 | * ["_foo_", "\\Elastic\\OpenApi\\Codegen\\Exception\\UnexpectedValueException"] 40 | */ 41 | public function testConnectionErrors($apiEndpoint, $exceptionClass) 42 | { 43 | $this->expectException($exceptionClass); 44 | $client = ClientBuilder::create($apiEndpoint, getenv('AS_PRIVATE_KEY'))->build(); 45 | $client->listEngines(); 46 | } 47 | 48 | /** 49 | * Test an Authentication exception is thrown when providing an in valid API Key. 50 | * 51 | * @expectedException \Elastic\OpenApi\Codegen\Exception\AuthenticationException 52 | */ 53 | public function testAuthenticationError() 54 | { 55 | $client = ClientBuilder::create(getenv('AS_URL'), 'not-an-api-key')->build(); 56 | $client->listEngines(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tests/Integration/CurationApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 36 | $engineName = $this->getDefaultEngineName(); 37 | 38 | $curation = $client->createCuration($engineName, $queries, $promotedIds, $hiddenIds); 39 | $this->assertArrayHasKey('id', $curation); 40 | 41 | $curation = $client->getCuration($engineName, $curation['id']); 42 | $this->assertEquals($queries, $curation['queries']); 43 | 44 | $curationListResponse = $client->listCurations($engineName); 45 | $this->assertEquals(1, $curationListResponse['meta']['page']['total_results']); 46 | $this->assertCount(1, $curationListResponse['results']); 47 | 48 | $updateResponse = $client->updateCuration($engineName, $curation['id'], $queries, $promotedIds, $hiddenIds); 49 | $this->assertArrayHasKey('id', $updateResponse); 50 | $this->assertEquals($curation['id'], $updateResponse['id']); 51 | 52 | $deleteOperationResponse = $client->deleteCuration($engineName, $curation['id']); 53 | $this->assertEquals(['deleted' => true], $deleteOperationResponse); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/Integration/DocumentApiTest.php: -------------------------------------------------------------------------------- 1 | getSampleDocuments(); 25 | $engineName = $this->getDefaultEngineName(); 26 | $client = $this->getDefaultClient(); 27 | 28 | $indexingResponse = $client->indexDocuments($engineName, $documents); 29 | $this->waitForIndexing(); 30 | 31 | $this->assertCount(count($documents), $indexingResponse); 32 | foreach ($indexingResponse as $documentIndexingResponse) { 33 | $this->assertEmpty($documentIndexingResponse['errors']); 34 | } 35 | } 36 | 37 | /** 38 | * Test getting documents after they have been indexed. 39 | */ 40 | public function testGetDocuments() 41 | { 42 | $documents = $this->getSampleDocuments(); 43 | $engineName = $this->getDefaultEngineName(); 44 | $client = $this->getDefaultClient(); 45 | $documentIds = array_column($documents, 'id'); 46 | 47 | $client->indexDocuments($engineName, $documents); 48 | $this->waitForIndexing(); 49 | 50 | $this->assertEquals($documents, $client->getDocuments($engineName, $documentIds)); 51 | } 52 | 53 | /** 54 | * Test listing documents after they have been indexed. 55 | */ 56 | public function testListDocuments() 57 | { 58 | $documents = $this->getSampleDocuments(); 59 | $engineName = $this->getDefaultEngineName(); 60 | $client = $this->getDefaultClient(); 61 | $client->indexDocuments($engineName, $documents); 62 | $this->waitForIndexing(); 63 | 64 | $documentListResponse = $client->listDocuments($engineName, 1, 25); 65 | 66 | $this->assertEquals(1, $documentListResponse['meta']['page']['current']); 67 | $this->assertEquals(25, $documentListResponse['meta']['page']['size']); 68 | $this->assertCount(count($documents), $documentListResponse['results']); 69 | } 70 | 71 | /** 72 | * Test delete documents after they have been indexed. 73 | */ 74 | public function testDeleteDocuments() 75 | { 76 | $documents = $this->getSampleDocuments(); 77 | $engineName = $this->getDefaultEngineName(); 78 | $client = $this->getDefaultClient(); 79 | $documentIds = array_column($documents, 'id'); 80 | $client->indexDocuments($engineName, $documents); 81 | $this->waitForIndexing(); 82 | 83 | $client->deleteDocuments($engineName, [current($documentIds)]); 84 | $this->waitForIndexing(); 85 | 86 | $documentListResponse = $client->listDocuments($engineName); 87 | 88 | $this->assertCount(count($documents) - 1, $documentListResponse['results']); 89 | } 90 | 91 | /** 92 | * Test delete documents after they have been indexed. 93 | */ 94 | public function testUpdatingDocuments() 95 | { 96 | $documents = $this->getSampleDocuments(); 97 | $engineName = $this->getDefaultEngineName(); 98 | $client = $this->getDefaultClient(); 99 | $client->updateSchema($engineName, ['title' => 'text']); 100 | $client->indexDocuments($engineName, $documents); 101 | 102 | $documentsUpdates = [['id' => $documents[0]['id'], 'title' => 'foo']]; 103 | $updateResponse = $client->updateDocuments($engineName, $documentsUpdates); 104 | 105 | $this->assertEmpty(current($updateResponse)['errors']); 106 | } 107 | 108 | /** 109 | * Test getting a document that does not exists. 110 | */ 111 | public function testGetNonExistingDocuments() 112 | { 113 | $this->assertEquals([null], $this->getDefaultClient()->getDocuments($this->getDefaultEngineName(), ['foo'])); 114 | } 115 | 116 | /** 117 | * Test index in an engine that does not exists. 118 | * 119 | * @expectedException \Elastic\OpenApi\Codegen\Exception\NotFoundException 120 | */ 121 | public function testIndexingInInvalidEngine() 122 | { 123 | $this->getDefaultClient()->getDocuments('not-an-engine', $this->getSampleDocuments()); 124 | } 125 | 126 | private function waitForIndexing() 127 | { 128 | sleep(10); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Tests/Integration/EngineApiTest.php: -------------------------------------------------------------------------------- 1 | engines as $engineName) { 32 | try { 33 | $this->getDefaultClient()->deleteEngine($engineName); 34 | } catch (NotFoundException $e) { 35 | // The engine have already been deleted. Nothing to do. 36 | } 37 | } 38 | $this->engines = []; 39 | } 40 | 41 | /** 42 | * This test run the following scenario: 43 | * - Create a new engine and check the name in the return. 44 | * - Retrieve this engine and test name and the language. 45 | * - Try to list the engines and check the new engine is present in the entries. 46 | * - Delete the engine and check the result. 47 | * 48 | * @param string $language Engine language. 49 | * 50 | * @testWith ["en"] 51 | * [null] 52 | */ 53 | public function testApiMethods($language) 54 | { 55 | $client = $this->getDefaultClient(); 56 | $engineName = $this->getEngineName(__METHOD__, func_get_args()); 57 | 58 | $this->assertEquals($engineName, $client->createEngine($engineName, $language)['name']); 59 | $this->engines[] = $engineName; 60 | 61 | $engine = $client->getEngine($engineName); 62 | $this->assertEquals($engineName, $engine['name']); 63 | $this->assertEquals($language, $engine['language']); 64 | 65 | $engineList = $client->listEngines(1, 20); 66 | $this->assertContains($engine, $engineList['results']); 67 | 68 | $this->assertTrue($client->deleteEngine($engineName)['deleted']); 69 | } 70 | 71 | /** 72 | * Try to get a non existing engine. 73 | * 74 | * @expectedException \Elastic\OpenApi\Codegen\Exception\NotFoundException 75 | */ 76 | public function testGetNonExistingEngine() 77 | { 78 | $this->getDefaultClient()->getEngine('some-non-existing-engine'); 79 | } 80 | 81 | /** 82 | * Try to delete a non existing engine. 83 | * 84 | * @expectedException \Elastic\OpenApi\Codegen\Exception\NotFoundException 85 | */ 86 | public function testDeleteNonExistingEngine() 87 | { 88 | $this->getDefaultClient()->getEngine('some-non-existing-engine'); 89 | } 90 | 91 | /** 92 | * Try to create an already existing engine. 93 | * 94 | * @expectedException \Elastic\OpenApi\Codegen\Exception\BadRequestException 95 | */ 96 | public function testCreateAlreadyExistingEngine() 97 | { 98 | $engineName = $this->getEngineName(__METHOD__); 99 | $this->engines[] = $engineName; 100 | 101 | $this->getDefaultClient()->createEngine($engineName); 102 | $this->getDefaultClient()->createEngine($engineName); 103 | } 104 | 105 | private function getEngineName($method, $params = []) 106 | { 107 | $nameParts = [$this->getDefaultEngineName()]; 108 | 109 | $methodParts = explode(':', $method); 110 | $nameParts[] = strtolower(end($methodParts)); 111 | 112 | $nameParts = array_merge($nameParts, array_filter($params)); 113 | 114 | return implode('-', $nameParts); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Tests/Integration/LogApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 37 | $engine = $this->getDefaultEngineName(); 38 | 39 | $fromDate = date('c', strtotime('yesterday')); 40 | $toDate = date('c'); 41 | 42 | $logs = $client->getApiLogs( 43 | $engine, 44 | $fromDate, 45 | $toDate, 46 | $currentPage, 47 | $pageSize, 48 | $query, 49 | $status, 50 | $method, 51 | $sortDir 52 | ); 53 | 54 | $this->assertArrayHasKey('results', $logs); 55 | 56 | if ($pageSize) { 57 | $this->assertEquals($logs['meta']['page']['current'], $currentPage ? $currentPage : 1); 58 | $this->assertEquals($logs['meta']['page']['size'], $pageSize); 59 | } 60 | 61 | if ($query) { 62 | $this->assertEquals($query, $logs['meta']['query']); 63 | } 64 | 65 | if ($status) { 66 | $this->assertEquals($status, $logs['meta']['filters']['status']); 67 | } 68 | 69 | if ($method) { 70 | $this->assertEquals($method, $logs['meta']['filters']['method']); 71 | } 72 | 73 | if ($sortDir) { 74 | $this->assertEquals($sortDir, $logs['meta']['sort_direction']); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/Integration/QuerySuggestionApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 37 | $engine = $this->getDefaultEngineName(); 38 | 39 | $suggestions = $client->querySuggestion($engine, $queryText, $fields, $size); 40 | 41 | $this->assertNotEmpty($suggestions['meta']['request_id']); 42 | $this->assertNotEmpty($suggestions['results']['documents']); 43 | 44 | if (null !== $size) { 45 | $this->assertLessThanOrEqual($size, count($suggestions['results']['documents'])); 46 | } 47 | 48 | array_walk($suggestions['results']['documents'], function ($suggestion) { 49 | $this->assertNotEmpty($suggestion['suggestion']); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/Integration/SchemaApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 30 | $engineName = $this->getDefaultEngineName(); 31 | 32 | $schema = $client->getSchema($engineName); 33 | $this->assertArrayHasKey('title', $schema); 34 | $this->assertEquals('text', $schema['title']); 35 | } 36 | 37 | /** 38 | * Test updating the schema. 39 | * 40 | * @param string $fieldName 41 | * @param string $fieldType 42 | * 43 | * @testWith ["string_field", "text"] 44 | * ["date_field", "date"] 45 | * ["number_field", "number"] 46 | * ["geo_field", "geolocation"] 47 | */ 48 | public function testUpdateSchema($fieldName, $fieldType) 49 | { 50 | $client = $this->getDefaultClient(); 51 | $engineName = $this->getDefaultEngineName(); 52 | $schema = $client->updateSchema($engineName, [$fieldName => $fieldType]); 53 | 54 | $this->assertArrayHasKey($fieldName, $schema); 55 | $this->assertEquals($fieldType, $schema[$fieldName]); 56 | } 57 | 58 | /** 59 | * Test invalid schema updates. 60 | * 61 | * @param string $fieldName 62 | * @param string $fieldType 63 | * 64 | * @expectedException \Elastic\OpenApi\Codegen\Exception\BadRequestException 65 | * 66 | * @testWith ["string_field", "not-a-valid-type"] 67 | * ["id", "number"] 68 | * ["12", "text"] 69 | * ["invalid field name", "text"] 70 | * ["_invalid_field_name", "text"] 71 | * ["invalid-field-name", "text"] 72 | * ["invalidFieldName", "text"] 73 | * ["invalid.field.name", "text"] 74 | * ["INVALID", "text"] 75 | */ 76 | public function testInvalidSchemaUpdate($fieldName, $fieldType) 77 | { 78 | $client = $this->getDefaultClient(); 79 | $engineName = $this->getDefaultEngineName(); 80 | $client->updateSchema($engineName, [$fieldName => $fieldType]); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tests/Integration/SearchApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient()->search($this->getDefaultEngineName(), $queryText, $searchParams); 38 | 39 | $this->assertArrayHasKey('meta', $searchResponse); 40 | $this->assertArrayHasKey('results', $searchResponse); 41 | $this->assertArrayHasKey('page', $searchResponse['meta']); 42 | $this->assertNotEmpty($searchResponse['meta']['request_id']); 43 | 44 | if (isset($searchParams['page']['size'])) { 45 | $this->assertEquals($searchParams['page']['size'], $searchResponse['meta']['page']['size']); 46 | $this->assertEquals($searchParams['page']['current'], $searchResponse['meta']['page']['current']); 47 | } 48 | 49 | $expectedResultCount = min( 50 | $searchResponse['meta']['page']['total_results'], 51 | $searchResponse['meta']['page']['size'] 52 | ); 53 | 54 | $this->assertCount($expectedResultCount, $searchResponse['results']); 55 | 56 | if ($expectedResultCount > 0) { 57 | $firstDoc = current($searchResponse['results']); 58 | $this->assertArrayHasKey('_meta', $firstDoc); 59 | $this->assertArrayHasKey('score', $firstDoc['_meta']); 60 | } 61 | } 62 | 63 | /** 64 | * Run simple filtered searches and check the number of results. 65 | * 66 | * @param array $filters Search filters. 67 | * @param int $expectedResultsCount Number of expected results in the sample data. 68 | * 69 | * @testWith [{"tags": ["Cats"]}, 2] 70 | * [{"tags": ["Copycat"]}, 1] 71 | * [{"tags": ["Copycat", "Hall Of Fame"]}, 2] 72 | * [{"any": [{"tags": ["Copycat"]}, {"tags": "Hall Of Fame"}]}, 2] 73 | * [{"all": [{"tags": ["Copycat"]}, {"tags": "Hall Of Fame"}]}, 0] 74 | * [{"all": [{"tags": ["Cats"]}], "none": [{"tags": "Hall Of Fame"}]}, 1] 75 | */ 76 | public function testFilteredSearch($filters, $expectedResultsCount) 77 | { 78 | $searchParams = ['filters' => $filters]; 79 | $searchResponse = $this->getDefaultClient()->search($this->getDefaultEngineName(), '', $searchParams); 80 | $this->assertCount($expectedResultsCount, $searchResponse['results']); 81 | } 82 | 83 | /** 84 | * Run simple facets searches and check the number of results. 85 | * 86 | * @param array $facets Search Facets. 87 | * @param int $expectedValueCount Number of values expected in the facet. 88 | * 89 | * @testWith [{"tags": {"type": "value"}}, 5] 90 | * [{"tags": [{"type": "value", "size": 3, "sort": {"value": "asc"}}]}, 3] 91 | */ 92 | public function testFacetedSearch($facets, $expectedValueCount) 93 | { 94 | $searchParams = ['facets' => $facets]; 95 | $searchResponse = $this->getDefaultClient()->search($this->getDefaultEngineName(), '', $searchParams); 96 | $this->assertArrayHasKey('facets', $searchResponse); 97 | 98 | foreach ($facets as $facetName => $facetDefinition) { 99 | if (!isset($facetDefinition['type'])) { 100 | $facetDefinition = current($facetDefinition); 101 | } 102 | 103 | $this->assertArrayHasKey($facetName, $searchResponse['facets']); 104 | $currentFacet = current($searchResponse['facets'][$facetName]); 105 | $this->assertEquals($facetDefinition['type'], $currentFacet['type']); 106 | $this->assertCount($expectedValueCount, $currentFacet['data']); 107 | } 108 | } 109 | 110 | /** 111 | * Run simple sorted searches against sample data and check the first result. 112 | * 113 | * @param array $sortOrder Sort order definition. 114 | * @param string $expectedFirstDocId Id of the first expected match. 115 | * 116 | * @testWith [{"title": "asc"}, "JNDFojsd02"] 117 | * [{"title": "desc"}, "INscMGmhmX4"] 118 | * [[{"title": "asc"}], "JNDFojsd02"] 119 | * [[{"title": "desc"}], "INscMGmhmX4"] 120 | * [[{"title": "asc"}, {"_score": "desc"}], "JNDFojsd02"] 121 | */ 122 | public function testSortedSearch($sortOrder, $expectedFirstDocId) 123 | { 124 | $searchParams = ['sort' => $sortOrder]; 125 | $searchResponse = $this->getDefaultClient()->search($this->getDefaultEngineName(), '', $searchParams); 126 | $this->assertEquals($expectedFirstDocId, $searchResponse['results'][0]['id']['raw']); 127 | } 128 | 129 | /** 130 | * Run simple searches against sample data using search fields and check the number of results. 131 | * 132 | * @param string $queryText Search query text. 133 | * @param array $searchFields Search fields. 134 | * @param int $expectedResultsCount Number of expected results in the sample data. 135 | * 136 | * @testWith ["cat", {"title": {}}, 2] 137 | * ["cat", {"title": {"weight": 1}}, 2] 138 | * ["cat", {"text" : {}}, 0] 139 | * ["cat", {"title": {"weight": 1}, "text": {}}, 2] 140 | */ 141 | public function testSearchFields($queryText, $searchFields, $expectedResultsCount) 142 | { 143 | $searchParams = ['search_fields' => $searchFields]; 144 | $searchResponse = $this->getDefaultClient()->search($this->getDefaultEngineName(), $queryText, $searchParams); 145 | $this->assertCount($expectedResultsCount, $searchResponse['results']); 146 | } 147 | 148 | /** 149 | * Run a multisearch and check the content of the response. 150 | */ 151 | public function testMultiSearch() 152 | { 153 | $queries = [['query' => ''], ['query' => 'cat']]; 154 | 155 | $searchResponses = $this->getDefaultClient()->multiSearch($this->getDefaultEngineName(), $queries); 156 | 157 | $this->assertCount(count($queries), $searchResponses); 158 | 159 | foreach ($searchResponses as $searchResponse) { 160 | $this->assertArrayHasKey('meta', $searchResponse); 161 | $this->assertArrayHasKey('results', $searchResponse); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Tests/Integration/SearchSettingsApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 30 | $engineName = $this->getDefaultEngineName(); 31 | 32 | $searchSettings = $client->getSearchSettings($engineName); 33 | 34 | $this->assertArrayHasKey('search_fields', $searchSettings); 35 | $this->assertNotEmpty($searchSettings['search_fields']); 36 | $this->assertArrayHasKey('id', $searchSettings['search_fields']); 37 | $this->assertEquals(1, $searchSettings['search_fields']['id']['weight']); 38 | 39 | $this->assertArrayHasKey('boosts', $searchSettings); 40 | $this->assertEmpty($searchSettings['boosts']); 41 | } 42 | 43 | /** 44 | * Test update search weights. 45 | * 46 | * @param array $searchFields 47 | * 48 | * @testWith [{"title": {"weight": 2}}] 49 | * [{"title": {}}] 50 | * [{"title": {"weight": 2.4}}] 51 | * [{"title": {"weight": 2}, "text": {"weight": 2}}] 52 | */ 53 | public function testUpdateSearchWeights($searchFields) 54 | { 55 | $client = $this->getDefaultClient(); 56 | $engineName = $this->getDefaultEngineName(); 57 | 58 | $client->updateSearchSettings($engineName, ['search_fields' => $searchFields]); 59 | 60 | $searchSettings = $client->getSearchSettings($engineName); 61 | $this->assertEquals($searchFields, $searchSettings['search_fields']); 62 | } 63 | 64 | /** 65 | * Test update search weights with invalid data. 66 | * 67 | * @param array $searchFields 68 | * 69 | * @expectedException \Elastic\OpenApi\Codegen\Exception\BadRequestException 70 | * 71 | * @testWith [{"not_a_valid_field": {"weight": 2}}] 72 | * [{"title": {"weight": "not-a-number"}}] 73 | * [{"number_field": {"weight": 2}}] 74 | * [{"date_field": {"weight": 2}}] 75 | */ 76 | public function testInvalidUpdateSearchWeights($searchFields) 77 | { 78 | $client = $this->getDefaultClient(); 79 | $engineName = $this->getDefaultEngineName(); 80 | 81 | $client->updateSchema($engineName, ['number_field' => 'number', 'date_field' => 'date']); 82 | $client->updateSearchSettings($engineName, ['search_fields' => $searchFields]); 83 | } 84 | 85 | /** 86 | * Test reset the search settings. 87 | */ 88 | public function testResetSearchSettings() 89 | { 90 | $searchSettings = $this->getDefaultClient()->resetSearchSettings($this->getDefaultEngineName()); 91 | 92 | $this->assertArrayHasKey('search_fields', $searchSettings); 93 | $this->assertNotEmpty($searchSettings['search_fields']); 94 | $this->assertArrayHasKey('id', $searchSettings['search_fields']); 95 | $this->assertEquals(1, $searchSettings['search_fields']['id']['weight']); 96 | 97 | $this->assertArrayHasKey('boosts', $searchSettings); 98 | $this->assertEmpty($searchSettings['boosts']); 99 | } 100 | 101 | /** 102 | * Test update search boosts. 103 | * 104 | * @param array $boosts 105 | * 106 | * @testWith [{}] 107 | * [{"tags" : {"type": "value", "value": "Cat"}}] 108 | * [{"tags" : {"type": "value", "value": "Cat", "operation": "multiply"}}] 109 | * [{"tags" : {"type": "value", "value": "Cat", "factor": 3}}] 110 | * [{"tags" : {"type": "value", "value": ["Cat"]}}] 111 | */ 112 | public function testUpdateBoosts($boosts) 113 | { 114 | $client = $this->getDefaultClient(); 115 | $engineName = $this->getDefaultEngineName(); 116 | 117 | $searchSettings = $client->getSearchSettings($engineName); 118 | $searchSettings['boosts'] = $boosts; 119 | $client->updateSearchSettings($engineName, $searchSettings); 120 | 121 | $searchSettings = $client->getSearchSettings($engineName); 122 | $this->assertCount(count($boosts), $searchSettings['boosts']); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Tests/Integration/SynonymApiTest.php: -------------------------------------------------------------------------------- 1 | getDefaultClient(); 29 | $engineName = $this->getDefaultEngineName(); 30 | 31 | $synonymSet = $client->createSynonymSet($engineName, $synonyms); 32 | $this->assertArrayHasKey('id', $synonymSet); 33 | 34 | $synonymSet = $client->getSynonymSet($engineName, $synonymSet['id']); 35 | $this->assertEquals($synonyms, $synonymSet['synonyms']); 36 | 37 | $synonymSetListResponse = $client->listSynonymSets($engineName); 38 | $this->assertEquals(1, $synonymSetListResponse['meta']['page']['total_results']); 39 | $this->assertCount(1, $synonymSetListResponse['results']); 40 | 41 | $deleteOperationResponse = $client->deleteSynonymSet($engineName, $synonymSet['id']); 42 | $this->assertEquals(['deleted' => true], $deleteOperationResponse); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/Integration/_data/sampleDocs.yml: -------------------------------------------------------------------------------- 1 | - id: INscMGmhmX4 2 | url: http://www.youtube.com/watch?v=v1uyQZNg2vE 3 | title: The Original Grumpy Cat 4 | text: this is a test 5 | tags: 6 | - LOL 7 | - Cats 8 | - Internet 9 | - Hall Of Fame 10 | 11 | - id: JNDFojsd02 12 | url: http://www.youtube.com/watch?v=tsdfhk2j 13 | title: Another Grumpy Cat 14 | text: this is also a test 15 | tags: 16 | - LOL 17 | - Copycat 18 | - Cats 19 | - Internet 20 | -------------------------------------------------------------------------------- /Tests/Unit/ClientBuilderTest.php: -------------------------------------------------------------------------------- 1 | build(); 33 | $this->assertInstanceOf(Client::class, $client); 34 | } 35 | 36 | /** 37 | * Check instantiation with valid endpoints. 38 | * 39 | * @dataProvider invalidApiEndpoints 40 | * 41 | * @expectedException \Elastic\OpenApi\Codegen\Exception\UnexpectedValueException 42 | * 43 | * @param string $apiEndpoint 44 | */ 45 | public function testInvalidEndpoints($apiEndpoint) 46 | { 47 | ClientBuilder::create($apiEndpoint, 'apiKey')->build(); 48 | } 49 | 50 | /** 51 | * @return array 52 | */ 53 | public function validApiEndpoints() 54 | { 55 | return [['https://test.com'], ['http://test.com'], ['http://test'], ['https://test'], ['test.com'], ['test']]; 56 | } 57 | 58 | /** 59 | * @return array 60 | */ 61 | public function invalidApiEndpoints() 62 | { 63 | return [['test_']]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/Unit/Connection/Handler/ApiErrorHandlerTest.php: -------------------------------------------------------------------------------- 1 | expectException($exceptionClass); 33 | $this->expectExceptionMessage($exceptionMessage); 34 | } 35 | 36 | $handler = new ApiErrorHandler( 37 | function ($request) use ($response) { 38 | return new CompletedFutureArray($response); 39 | } 40 | ); 41 | 42 | $handlerResponse = $handler([])->wait(); 43 | 44 | if (null == $exceptionClass) { 45 | $this->assertEquals($response, $handlerResponse); 46 | } 47 | } 48 | 49 | /** 50 | * Test if the ApiRateExceededException excecption contains valid limit and retry values. 51 | * 52 | * @param int $limit 53 | * @param int $retryAfter 54 | */ 55 | public function testApiRateLimited($limit = 10, $retryAfter = 20) 56 | { 57 | $headers = [ 58 | RateLimitLoggingHandler::RATE_LIMIT_LIMIT_HEADER_NAME => [$limit], 59 | RateLimitLoggingHandler::RETRY_AFTER_HEADER_NAME => [$retryAfter], 60 | ]; 61 | $handler = new ApiErrorHandler( 62 | function ($request) use ($headers) { 63 | return new CompletedFutureArray(['status' => 429, 'headers' => $headers]); 64 | } 65 | ); 66 | 67 | try { 68 | $handler([])->wait(); 69 | } catch (\Elastic\AppSearch\Client\Exception\ApiRateExceededException $e) { 70 | $this->assertEquals($limit, $e->getApiRateLimit()); 71 | $this->assertEquals($retryAfter, $e->getRetryAfter()); 72 | } 73 | } 74 | 75 | /** 76 | * @return array 77 | */ 78 | public function errorDataProvider() 79 | { 80 | $parser = new \Symfony\Component\Yaml\Parser(); 81 | 82 | return $parser->parseFile(__DIR__ . '/_data/apiError.yml'); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Tests/Unit/Connection/Handler/RateLimitLoggingHandlerTest.php: -------------------------------------------------------------------------------- 1 | array_filter($this->getResponseHeaders($limit, $remaining))]; 36 | $handler = $this->getHandler($response); 37 | 38 | $handler([])->wait(); 39 | 40 | if ($this->shouldLogWarning($limit, $remaining)) { 41 | $this->assertNotEmpty($this->logArray); 42 | $this->assertArrayHasKey('warning', $this->logArray); 43 | } else { 44 | $this->assertEmpty($this->logArray); 45 | } 46 | } 47 | 48 | /** 49 | * Return a the response handler used in test. 50 | * 51 | * @param array $response 52 | * 53 | * @return \Elastic\AppSearch\Client\Connection\Handler\RateLimitLoggingHandler 54 | */ 55 | private function getHandler($response) 56 | { 57 | $responseCallback = function ($request) use ($response) { 58 | return new CompletedFutureArray($response); 59 | }; 60 | 61 | return new RateLimitLoggingHandler($responseCallback, $this->getLoggerMock()); 62 | } 63 | 64 | /** 65 | * Indicate if a warning should be logged or not. 66 | * 67 | * @param int|null $limit 68 | * @param int|null $remaining 69 | * 70 | * @return bool 71 | */ 72 | private function shouldLogWarning($limit, $remaining) 73 | { 74 | return $limit && ($remaining / $limit) < RateLimitLoggingHandler::RATE_LIMIT_PERCENT_WARNING_TRESHOLD; 75 | } 76 | 77 | /** 78 | * Return response headers. 79 | * 80 | * @param int|null $limit 81 | * @param int|null $remaining 82 | * 83 | * @return array[] 84 | */ 85 | private function getResponseHeaders($limit, $remaining) 86 | { 87 | return [ 88 | RateLimitLoggingHandler::RATE_LIMIT_LIMIT_HEADER_NAME => [$limit], 89 | RateLimitLoggingHandler::RATE_LIMIT_REMAINING_HEADER_NAME => [$remaining], 90 | ]; 91 | } 92 | 93 | /** 94 | * Create a mock for the logger interface. 95 | * 96 | * @return \Psr\Log\LoggerInterface 97 | */ 98 | private function getLoggerMock() 99 | { 100 | $this->logArray = []; 101 | 102 | $logger = $this->createMock(LoggerInterface::class); 103 | $logger->method('warning')->willReturnCallback(function ($message) { 104 | $this->logArray['warning'][] = $message; 105 | }); 106 | 107 | return $logger; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Tests/Unit/Connection/Handler/RequestAuthenticationHandlerTest.php: -------------------------------------------------------------------------------- 1 | assertArrayHasKey('Authorization', $response); 34 | $this->assertEquals('Bearer apiKey', current($response['Authorization'])); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Unit/Connection/Handler/RequestClientHeaderHandlerTest.php: -------------------------------------------------------------------------------- 1 | assertArrayHasKey('X-Swiftype-Client', $response); 35 | $this->assertArrayHasKey('X-Swiftype-Client-Version', $response); 36 | } 37 | 38 | /** 39 | * Check the reporting headers is present and contains the right value. 40 | */ 41 | public function testAddAuthHeaderWithIntegrationName() 42 | { 43 | $handler = function ($request) { 44 | return $request['headers']; 45 | }; 46 | 47 | $requestHandler = new RequestClientHeaderHandler($handler, 'integration:2.1.1'); 48 | $response = $requestHandler([]); 49 | 50 | $this->assertArrayHasKey('X-Swiftype-Client', $response); 51 | $this->assertArrayHasKey('X-Swiftype-Client-Version', $response); 52 | $this->assertArrayHasKey('X-Swiftype-Integration', $response); 53 | $this->assertArrayHasKey('X-Swiftype-Integration-Version', $response); 54 | $this->assertEquals(['integration'], $response['X-Swiftype-Integration']); 55 | $this->assertEquals(['2.1.1'], $response['X-Swiftype-Integration-Version']); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/Unit/Connection/Handler/_data/apiError.yml: -------------------------------------------------------------------------------- 1 | - 2 | - status: 200 3 | foo: bar 4 | - null 5 | - null 6 | 7 | - 8 | - status: 400 9 | foo: bar 10 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 11 | - Unexpected error. 12 | 13 | - 14 | - status: 400 15 | reason: Error msg. 16 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 17 | - Error msg. 18 | 19 | - 20 | - status: 400 21 | reason: Error msg. 22 | body: 23 | error: Body error msg. 24 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 25 | - Body error msg. 26 | 27 | - 28 | - status: 400 29 | body: 30 | error: Error msg. 31 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 32 | - Error msg. 33 | 34 | - 35 | - status: 400 36 | body: 37 | errors: Error msg. 38 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 39 | - Error msg. 40 | 41 | - 42 | - status: 400 43 | body: 44 | error: 45 | - Error msg. 46 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 47 | - Error msg. 48 | 49 | - 50 | - status: 400 51 | body: 52 | errors: 53 | - Error msg1. 54 | - Error msg2. 55 | - \Elastic\OpenApi\Codegen\Exception\BadRequestException 56 | - Error msg1. Error msg2. 57 | 58 | - 59 | - status: 401 60 | body: 61 | error: Error msg. 62 | - \Elastic\OpenApi\Codegen\Exception\AuthenticationException 63 | - Error msg. 64 | 65 | - 66 | - status: 403 67 | body: 68 | error: Error msg. 69 | - \Elastic\OpenApi\Codegen\Exception\AuthenticationException 70 | - Error msg. 71 | 72 | - 73 | - status: 404 74 | body: 75 | error: Error msg. 76 | - \Elastic\OpenApi\Codegen\Exception\NotFoundException 77 | - Error msg. 78 | 79 | - 80 | - status: 429 81 | body: 82 | error: Error msg. 83 | - \Elastic\AppSearch\Client\Exception\ApiRateExceededException 84 | - Error msg. 85 | 86 | - 87 | - status: 500 88 | body: 89 | error: Error msg. 90 | - \Elastic\OpenApi\Codegen\Exception\ApiException 91 | - Error msg. 92 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "elastic/app-search", 3 | "description" : "Elastic App Search Official PHP Client", 4 | "homepage": "https://github.com/elastic/app-search-php", 5 | "keywords" : [ 6 | "search", 7 | "client", 8 | "elastic", 9 | "php" 10 | ], 11 | "license" : "Apache-2.0", 12 | "type" : "library", 13 | "authors" : [ 14 | {"name" : "Aurélien FOUCRET", "email": "aurelien.foucret@elastic.co"} 15 | ], 16 | "require" : { 17 | "php" : "^5.6|^7.0", 18 | "elastic/openapi-codegen" : "^1.0.4", 19 | "psr/log" : "^1.0." 20 | }, 21 | "require-dev" : { 22 | "phpunit/phpunit" : "^5.6.0|^6.3.0", 23 | "squizlabs/php_codesniffer" : "^3.4.0", 24 | "symfony/yaml" : "*", 25 | "friendsofphp/php-cs-fixer": "^2.14", 26 | "overtrue/phplint": "^1.1" 27 | }, 28 | "autoload" : { 29 | "psr-4" : { 30 | "Elastic\\AppSearch\\Client\\" : "" 31 | }, 32 | "exclude-from-classmap" : [ 33 | "/Tests/" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # A docker-compose to make it easier to run PHP integration tests locally with different PHP versions. 2 | version: "2" 3 | 4 | services: 5 | 6 | elasticsearch: 7 | image: docker.elastic.co/elasticsearch/elasticsearch:7.13.0 8 | environment: 9 | - "discovery.type=single-node" 10 | - "ES_JAVA_OPTS=-Xms512m -Xmx512m" 11 | - "xpack.security.enabled=true" 12 | - "xpack.security.authc.api_key.enabled=true" 13 | - "ELASTIC_PASSWORD=password" 14 | ulimits: 15 | memlock: 16 | soft: -1 17 | hard: -1 18 | ports: 19 | - 9200:9200 20 | 21 | entsearch: 22 | image: docker.elastic.co/enterprise-search/enterprise-search:7.13.0 23 | depends_on: 24 | - "elasticsearch" 25 | environment: 26 | - "ENT_SEARCH_DEFAULT_PASSWORD=password" 27 | - "elasticsearch.username=elastic" 28 | - "elasticsearch.password=password" 29 | - "elasticsearch.host=http://elasticsearch:9200" 30 | - "allow_es_settings_modification=true" 31 | - "secret_management.encryption_keys=[4a2cd3f81d39bf28738c10db0ca782095ffac07279561809eecc722e0c20eb09]" 32 | - "elasticsearch.startup_retry.interval=15" 33 | ports: 34 | - 3002:3002 35 | 36 | # php_client - Helpful for running integration tests locally. 37 | # docker-compose build --build-arg base_image=php:7.3-cli 38 | # docker-compose run php_client bash 39 | # # source .circleci/retrieve-credentials.sh 40 | # # vendor/bin/phpunit -c phpunit.xml.dist --testsuite integration 41 | php_client: 42 | build: 43 | context: . 44 | args: 45 | base_image: php:7.3-cli 46 | image: php_client 47 | depends_on: 48 | - "elasticsearch" 49 | - "entsearch" 50 | environment: 51 | - "AS_URL=http://entsearch:3002" 52 | - "ES_URL=http://elasticsearch:9200" 53 | - "AS_ENGINE_NAME=php-integration-test-7.3" 54 | -------------------------------------------------------------------------------- /logo-app-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/app-search-php/1b8a8610e0efe812975cc445c0cd46c70cb340ea/logo-app-search.png -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The coding standard for Elastic App Search Client. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ./Tests/Unit 18 | 19 | 20 | ./Tests/Integration 21 | 22 | 23 | 24 | 25 | 26 | ./ 27 | 28 | ./resources 29 | ./Tests/ 30 | ./vendor 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /resources/api/api-spec.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.2 2 | 3 | info: 4 | title: "Elastic App Search API" 5 | description: "Elastic App Search API" 6 | version: "1.0" 7 | 8 | externalDocs: 9 | url: https://swiftype.com/documentation/app-search 10 | 11 | components: 12 | # Reusable schemas definitions ----------------------------------------------- 13 | schemas: 14 | simpleObject: 15 | type: object 16 | 17 | documentId: 18 | type: string 19 | 20 | documentIds: 21 | type: array 22 | items: 23 | $ref: "#/components/schemas/documentId" 24 | 25 | simpleFieldValue: 26 | anyOf: 27 | - type: string 28 | - type: number 29 | 30 | documentFieldValue: 31 | anyOf: 32 | - $ref: "#/components/schemas/simpleFieldValue" 33 | - type: array 34 | items: 35 | $ref: "#/components/schemas/simpleFieldValue" 36 | 37 | document: 38 | type: object 39 | required: 40 | - id 41 | properties: 42 | id: 43 | type: string 44 | additionalProperties: 45 | $ref: "#/components/schemas/documentFieldValue" 46 | 47 | documentList: 48 | type: array 49 | items: 50 | $ref: "#/components/schemas/document" 51 | 52 | schemaData: 53 | type: object 54 | additionalProperties: 55 | type: string 56 | 57 | searchBoosts: 58 | type: object 59 | #TODO 60 | 61 | searchFields: 62 | type: object 63 | additionalProperties: 64 | type: object 65 | additionalProperties: false 66 | properties: 67 | weight: 68 | type: integer 69 | 70 | searchSettings: 71 | type: object 72 | additionalProperties: false 73 | properties: 74 | search_fields: 75 | $ref: "#/components/schemas/searchFields" 76 | boosts: 77 | $ref: "#/components/schemas/searchBoosts" 78 | 79 | searchPageParam: 80 | type: object 81 | additionalProperties: false 82 | properties: 83 | current: 84 | type: integer 85 | size: 86 | type: integer 87 | 88 | searchRequestParams: 89 | type: object 90 | additionalProperties: false 91 | properties: 92 | query: 93 | type: string 94 | page: 95 | $ref: "#/components/schemas/searchPageParam" 96 | filters: 97 | $ref: "#/components/schemas/simpleObject" #TODO 98 | facets: 99 | $ref: "#/components/schemas/simpleObject" #TODO 100 | search_fields: 101 | $ref: "#/components/schemas/simpleObject" #TODO 102 | boost: 103 | $ref: "#/components/schemas/simpleObject" #TODO 104 | result_fields: 105 | $ref: "#/components/schemas/simpleObject" #TODO 106 | sort: 107 | $ref: "#/components/schemas/simpleObject" #TODO 108 | group: 109 | $ref: "#/components/schemas/simpleObject" #TODO 110 | analytics: 111 | $ref: "#/components/schemas/simpleObject" #TODO 112 | 113 | searchRequestList: 114 | type: array 115 | items: 116 | $ref: "#/components/schemas/searchRequestParams" 117 | 118 | synonymList: 119 | type: array 120 | minLength: 2 121 | items: 122 | type: string 123 | 124 | # Reusable request bodies ---------------------------------------------------- 125 | requestBodies: 126 | documentIdsRequest: 127 | required: true 128 | description: List of document ids. 129 | content: 130 | application/json: 131 | schema: 132 | $ref: "#/components/schemas/documentIds" 133 | 134 | engineIdsRequest: 135 | required: true 136 | description: List of engine ids. 137 | content: 138 | application/json: 139 | schema: 140 | $ref: "#/components/schemas/documentIds" 141 | 142 | documentsIndexingRequest: 143 | required: true 144 | description: List of document to index. 145 | content: 146 | application/json: 147 | schema: 148 | $ref: "#/components/schemas/documentList" 149 | 150 | documentsPartialUpdateRequest: 151 | required: true 152 | description: List of document to update. 153 | content: 154 | application/json: 155 | schema: 156 | $ref: "#/components/schemas/documentList" 157 | 158 | schemaUpdateRequest: 159 | required: true 160 | description: Schema description. 161 | content: 162 | application/json: 163 | schema: 164 | $ref: "#/components/schemas/schemaData" 165 | 166 | searchRequestParams: 167 | description: Search request parameters. 168 | content: 169 | application/json: 170 | schema: 171 | $ref: "#/components/schemas/searchRequestParams" 172 | 173 | searchSettingsRequest: 174 | required: true 175 | description: Search settings. 176 | content: 177 | application/json: 178 | schema: 179 | $ref: "#/components/schemas/searchSettings" 180 | 181 | # Reusable parameters definitions -------------------------------------------- 182 | parameters: 183 | engineNameParam: 184 | name: engine_name 185 | in: path 186 | description: Name of the engine. 187 | required: true 188 | schema: 189 | type: string 190 | 191 | currentPage: 192 | name: page.current 193 | in: query 194 | description: The page to fetch. Defaults to 1. 195 | schema: 196 | type: string 197 | x-codegen-param-name: currentPage 198 | 199 | pageSize: 200 | name: page.size 201 | in: query 202 | description: The number of results per page. 203 | schema: 204 | type: string 205 | 206 | searchQueryText: 207 | name: query 208 | in: query 209 | x-codegen-param-name: queryText 210 | description: Search query text. 211 | required: true 212 | schema: 213 | type: string 214 | 215 | synonymSetId: 216 | name: synonym_set_id 217 | in: path 218 | description: Synonym set id. 219 | required: true 220 | schema: 221 | type: string 222 | 223 | curationId: 224 | name: curation_id 225 | in: path 226 | description: Curation id. 227 | required: true 228 | schema: 229 | type: string 230 | 231 | # Reusable responses definitions --------------------------------------------- 232 | responses: 233 | jsonResponse: 234 | description: A simple JSON response. 235 | content: 236 | application/json: 237 | schema: 238 | $ref: "#/components/schemas/simpleObject" 239 | 240 | paths: 241 | /engines: 242 | get: 243 | operationId: listEngines 244 | summary: Retrieves all engines with optional pagination support. 245 | externalDocs: 246 | url: https://swiftype.com/documentation/app-search/api/engines#list 247 | tags: 248 | - Engine API 249 | parameters: 250 | - $ref: "#/components/parameters/currentPage" 251 | - $ref: "#/components/parameters/pageSize" 252 | responses: 253 | default: 254 | $ref: "#/components/responses/jsonResponse" 255 | post: 256 | operationId: doCreateEngine 257 | x-operation-scope: protected 258 | summary: Creates a new engine. 259 | externalDocs: 260 | url: https://swiftype.com/documentation/app-search/api/engines#create 261 | tags: 262 | - Engine API 263 | parameters: 264 | - name: name 265 | in: query 266 | required: true 267 | description: Engine name. 268 | schema: 269 | type: string 270 | - name: language 271 | in: query 272 | description: Engine language (null for universal). 273 | schema: 274 | type: string 275 | - name: type 276 | in: query 277 | description: Engine type. 278 | schema: 279 | type: string 280 | default: 'default' 281 | - name: source_engines 282 | in: query 283 | description: Sources engines list. 284 | schema: 285 | type: array 286 | items: 287 | type: string 288 | responses: 289 | default: 290 | $ref: "#/components/responses/jsonResponse" 291 | 292 | /engines/{engine_name}: 293 | parameters: 294 | - $ref: "#/components/parameters/engineNameParam" 295 | get: 296 | operationId: getEngine 297 | summary: Retrieves an engine by name. 298 | externalDocs: 299 | url: https://swiftype.com/documentation/app-search/api/engines#get 300 | tags: 301 | - Engine API 302 | responses: 303 | default: 304 | $ref: "#/components/responses/jsonResponse" 305 | delete: 306 | operationId: deleteEngine 307 | summary: Delete an engine by name. 308 | externalDocs: 309 | url: https://swiftype.com/documentation/app-search/api/engines#delete 310 | tags: 311 | - Engine API 312 | responses: 313 | default: 314 | $ref: "#/components/responses/jsonResponse" 315 | 316 | /engines/{engine_name}/source_engines: 317 | parameters: 318 | - $ref: "#/components/parameters/engineNameParam" 319 | post: 320 | operationId: addMetaEngineSource 321 | summary: Add a source engine to an existing meta engine. 322 | externalDocs: 323 | url: https://swiftype.com/documentation/app-search/api/meta-engines#add-source-engines 324 | tags: 325 | - Engine API 326 | requestBody: 327 | $ref: "#/components/requestBodies/engineIdsRequest" 328 | x-codegen-request-body-name: sourceEngines 329 | responses: 330 | default: 331 | $ref: "#/components/responses/jsonResponse" 332 | delete: 333 | operationId: deleteMetaEngineSource 334 | summary: Delete a source engine from a meta engine. 335 | externalDocs: 336 | url: https://swiftype.com/documentation/app-search/api/meta-engines#remove-source-engines 337 | tags: 338 | - Engine API 339 | requestBody: 340 | $ref: "#/components/requestBodies/engineIdsRequest" 341 | x-codegen-request-body-name: sourceEngines 342 | responses: 343 | default: 344 | $ref: "#/components/responses/jsonResponse" 345 | 346 | /engines/{engine_name}/documents: 347 | parameters: 348 | - $ref: "#/components/parameters/engineNameParam" 349 | get: 350 | operationId: getDocuments 351 | summary: Retrieves one or more documents by id. 352 | externalDocs: 353 | url: https://swiftype.com/documentation/app-search/api/documents#get 354 | tags: 355 | - Documents API 356 | requestBody: 357 | $ref: "#/components/requestBodies/documentIdsRequest" 358 | x-codegen-request-body-name: documentIds 359 | responses: 360 | default: 361 | $ref: "#/components/responses/jsonResponse" 362 | post: 363 | operationId: indexDocuments 364 | summary: Create or update documents. 365 | externalDocs: 366 | url: https://swiftype.com/documentation/app-search/api/documents#create 367 | tags: 368 | - Documents API 369 | requestBody: 370 | $ref: "#/components/requestBodies/documentsIndexingRequest" 371 | x-codegen-request-body-name: documents 372 | responses: 373 | default: 374 | $ref: "#/components/responses/jsonResponse" 375 | patch: 376 | operationId: updateDocuments 377 | summary: Partial update of documents. 378 | externalDocs: 379 | url: https://swiftype.com/documentation/app-search/api/documents#partial 380 | tags: 381 | - Documents API 382 | requestBody: 383 | $ref: "#/components/requestBodies/documentsPartialUpdateRequest" 384 | x-codegen-request-body-name: documents 385 | responses: 386 | default: 387 | $ref: "#/components/responses/jsonResponse" 388 | delete: 389 | operationId: deleteDocuments 390 | summary: Delete documents by id. 391 | externalDocs: 392 | url: https://swiftype.com/documentation/app-search/api/documents#partial 393 | tags: 394 | - Documents API 395 | requestBody: 396 | $ref: "#/components/requestBodies/documentIdsRequest" 397 | x-codegen-request-body-name: documentIds 398 | responses: 399 | default: 400 | $ref: "#/components/responses/jsonResponse" 401 | 402 | /engines/{engine_name}/documents/list: 403 | parameters: 404 | - $ref: "#/components/parameters/engineNameParam" 405 | get: 406 | operationId: listDocuments 407 | summary: List all available documents with optional pagination support. 408 | externalDocs: 409 | url: https://swiftype.com/documentation/app-search/api/documents#list 410 | tags: 411 | - Documents API 412 | parameters: 413 | - $ref: "#/components/parameters/currentPage" 414 | - $ref: "#/components/parameters/pageSize" 415 | responses: 416 | default: 417 | $ref: "#/components/responses/jsonResponse" 418 | 419 | /engines/{engine_name}/schema: 420 | parameters: 421 | - $ref: "#/components/parameters/engineNameParam" 422 | get: 423 | operationId: getSchema 424 | summary: Retrieve current schema for then engine. 425 | externalDocs: 426 | url: https://swiftype.com/documentation/app-search/api/schema#read 427 | tags: 428 | - Schema API 429 | responses: 430 | default: 431 | $ref: "#/components/responses/jsonResponse" 432 | post: 433 | operationId: updateSchema 434 | summary: Update schema for the current engine. 435 | externalDocs: 436 | url: https://swiftype.com/documentation/app-search/api/schema#patch 437 | tags: 438 | - Schema API 439 | requestBody: 440 | $ref: "#/components/requestBodies/schemaUpdateRequest" 441 | x-codegen-request-body-name: schema 442 | responses: 443 | default: 444 | $ref: "#/components/responses/jsonResponse" 445 | 446 | /engines/{engine_name}/search: 447 | parameters: 448 | - $ref: "#/components/parameters/engineNameParam" 449 | post: 450 | operationId: search 451 | summary: Allows you to search over, facet and filter your data. 452 | externalDocs: 453 | url: https://swiftype.com/documentation/app-search/api/search 454 | tags: 455 | - Search API 456 | parameters: 457 | - $ref: "#/components/parameters/searchQueryText" 458 | requestBody: 459 | $ref: "#/components/requestBodies/searchRequestParams" 460 | responses: 461 | default: 462 | $ref: "#/components/responses/jsonResponse" 463 | 464 | /engines/{engine_name}/multi_search: 465 | parameters: 466 | - $ref: "#/components/parameters/engineNameParam" 467 | post: 468 | operationId: multiSearch 469 | summary: Run several search in the same request. 470 | externalDocs: 471 | url: https://swiftype.com/documentation/app-search/api/search#multi 472 | tags: 473 | - Search API 474 | parameters: 475 | - name: queries 476 | in: query 477 | required: true 478 | description: Search queries. 479 | schema: 480 | $ref: "#/components/schemas/searchRequestList" 481 | responses: 482 | default: 483 | $ref: "#/components/responses/jsonResponse" 484 | 485 | /engines/{engine_name}/query_suggestion: 486 | parameters: 487 | - $ref: "#/components/parameters/engineNameParam" 488 | post: 489 | operationId: querySuggestion 490 | summary: Provide relevant query suggestions for incomplete queries. 491 | externalDocs: 492 | url: https://swiftype.com/documentation/app-search/api/query-suggestion 493 | tags: 494 | - Query suggestion API 495 | parameters: 496 | - name: query 497 | in: query 498 | required: true 499 | description: A partial query for which to receive suggestions. 500 | schema: 501 | type: string 502 | - name: types.documents.fields 503 | in: query 504 | description: List of fields to use to generate suggestions. Defaults to all text fields. 505 | x-codegen-param-name: fields 506 | schema: 507 | type: array 508 | items: 509 | type: string 510 | - name: size 511 | in: query 512 | description: Number of query suggestions to return. Must be between 1 and 20. Defaults to 5. 513 | schema: 514 | type: integer 515 | responses: 516 | default: 517 | $ref: "#/components/responses/jsonResponse" 518 | 519 | /engines/{engine_name}/search_settings: 520 | parameters: 521 | - $ref: "#/components/parameters/engineNameParam" 522 | get: 523 | operationId: getSearchSettings 524 | summary: Retrive current search settings for the engine. 525 | externalDocs: 526 | url: https://swiftype.com/documentation/app-search/api/search-settings#show 527 | tags: 528 | - Search settings API 529 | responses: 530 | default: 531 | $ref: "#/components/responses/jsonResponse" 532 | put: 533 | operationId: updateSearchSettings 534 | summary: Update search settings for the engine. 535 | externalDocs: 536 | url: https://swiftype.com/documentation/app-search/api/search-settings#update 537 | tags: 538 | - Search settings API 539 | requestBody: 540 | $ref: "#/components/requestBodies/searchSettingsRequest" 541 | responses: 542 | default: 543 | $ref: "#/components/responses/jsonResponse" 544 | 545 | /engines/{engine_name}/search_settings/reset: 546 | parameters: 547 | - $ref: "#/components/parameters/engineNameParam" 548 | post: 549 | operationId: resetSearchSettings 550 | summary: Reset search settings for the engine. 551 | externalDocs: 552 | url: https://swiftype.com/documentation/app-search/api/search-settings#reset 553 | tags: 554 | - Search settings API 555 | responses: 556 | default: 557 | $ref: "#/components/responses/jsonResponse" 558 | 559 | /engines/{engine_name}/synonyms: 560 | parameters: 561 | - $ref: "#/components/parameters/engineNameParam" 562 | get: 563 | operationId: listSynonymSets 564 | summary: Retrieve available synonym sets for the engine. 565 | externalDocs: 566 | url: https://swiftype.com/documentation/app-search/api/synonyms#get 567 | tags: 568 | - Synonyms API 569 | parameters: 570 | - $ref: "#/components/parameters/currentPage" 571 | - $ref: "#/components/parameters/pageSize" 572 | responses: 573 | default: 574 | $ref: "#/components/responses/jsonResponse" 575 | post: 576 | operationId: createSynonymSet 577 | summary: Create a new synonym set. 578 | externalDocs: 579 | url: https://swiftype.com/documentation/app-search/api/synonyms#create 580 | tags: 581 | - Synonyms API 582 | parameters: 583 | - name: synonyms 584 | in: query 585 | required: true 586 | description: List of synonyms words. 587 | schema: 588 | $ref: "#/components/schemas/synonymList" 589 | responses: 590 | default: 591 | $ref: "#/components/responses/jsonResponse" 592 | 593 | /engines/{engine_name}/synonyms/{synonym_set_id}: 594 | parameters: 595 | - $ref: "#/components/parameters/engineNameParam" 596 | - $ref: "#/components/parameters/synonymSetId" 597 | get: 598 | operationId: getSynonymSet 599 | summary: Retrieve a synonym set by id. 600 | externalDocs: 601 | url: https://swiftype.com/documentation/app-search/api/synonyms#list-one 602 | tags: 603 | - Synonyms API 604 | responses: 605 | default: 606 | $ref: "#/components/responses/jsonResponse" 607 | delete: 608 | operationId: deleteSynonymSet 609 | summary: Delete a synonym set by id. 610 | externalDocs: 611 | url: https://swiftype.com/documentation/app-search/api/synonyms#delete 612 | tags: 613 | - Synonyms API 614 | responses: 615 | default: 616 | $ref: "#/components/responses/jsonResponse" 617 | 618 | /engines/{engine_name}/curations: 619 | parameters: 620 | - $ref: "#/components/parameters/engineNameParam" 621 | get: 622 | operationId: listCurations 623 | summary: Retrieve available curations for the engine. 624 | externalDocs: 625 | url: https://swiftype.com/documentation/app-search/api/curations#read 626 | tags: 627 | - Curations API 628 | parameters: 629 | - $ref: "#/components/parameters/currentPage" 630 | - $ref: "#/components/parameters/pageSize" 631 | responses: 632 | default: 633 | $ref: "#/components/responses/jsonResponse" 634 | post: 635 | operationId: createCuration 636 | summary: Create a new curation. 637 | externalDocs: 638 | url: https://swiftype.com/documentation/app-search/api/curations#create 639 | tags: 640 | - Curations API 641 | parameters: 642 | - name: queries 643 | in: query 644 | required: true 645 | description: List of affected search queries. 646 | schema: 647 | type: array 648 | items: 649 | type: string 650 | - name: promoted 651 | in: query 652 | description: List of promoted document ids. 653 | x-codegen-param-name: promotedDocIds 654 | schema: 655 | $ref: "#/components/schemas/documentIds" 656 | - name: hidden 657 | in: query 658 | description: List of hidden document ids. 659 | x-codegen-param-name: hiddenDocIds 660 | schema: 661 | $ref: "#/components/schemas/documentIds" 662 | responses: 663 | default: 664 | $ref: "#/components/responses/jsonResponse" 665 | 666 | /engines/{engine_name}/curations/{curation_id}: 667 | parameters: 668 | - $ref: "#/components/parameters/engineNameParam" 669 | - $ref: "#/components/parameters/curationId" 670 | get: 671 | operationId: getCuration 672 | summary: Retrieve a curation by id. 673 | externalDocs: 674 | url: https://swiftype.com/documentation/app-search/api/curations#single 675 | tags: 676 | - Curations API 677 | responses: 678 | default: 679 | $ref: "#/components/responses/jsonResponse" 680 | put: 681 | operationId: updateCuration 682 | summary: Update an existing curation. 683 | externalDocs: 684 | url: https://swiftype.com/documentation/app-search/api/curations#update 685 | tags: 686 | - Curations API 687 | parameters: 688 | - name: queries 689 | in: query 690 | required: true 691 | description: List of affected search queries. 692 | schema: 693 | type: array 694 | items: 695 | type: string 696 | - name: promoted 697 | in: query 698 | description: List of promoted document ids. 699 | x-codegen-param-name: promotedDocIds 700 | schema: 701 | $ref: "#/components/schemas/documentIds" 702 | - name: hidden 703 | in: query 704 | description: List of hidden document ids. 705 | x-codegen-param-name: hiddenDocIds 706 | schema: 707 | $ref: "#/components/schemas/documentIds" 708 | responses: 709 | default: 710 | $ref: "#/components/requestBodies/curationResponse" 711 | delete: 712 | operationId: deleteCuration 713 | summary: Delete a curation by id. 714 | externalDocs: 715 | url: https://swiftype.com/documentation/app-search/api/curations#destroy 716 | tags: 717 | - Curations API 718 | responses: 719 | default: 720 | $ref: "#/components/responses/jsonResponse" 721 | 722 | /engines/{engine_name}/click: 723 | parameters: 724 | - $ref: "#/components/parameters/engineNameParam" 725 | post: 726 | operationId: logClickthrough 727 | summary: Send data about clicked results. 728 | externalDocs: 729 | url: https://swiftype.com/documentation/app-search/api/clickthrough 730 | tags: 731 | - Click API 732 | parameters: 733 | - $ref: "#/components/parameters/searchQueryText" 734 | - name: document_id 735 | in: query 736 | required: true 737 | description: The id of the document that was clicked on. 738 | schema: 739 | $ref: "#/components/schemas/documentId" 740 | - name: request_id 741 | in: query 742 | description: The request id returned in the meta tag of a search API response. 743 | schema: 744 | type: string 745 | - name: tags 746 | in: query 747 | description: Array of strings representing additional information you wish to track with the clickthrough. 748 | schema: 749 | type: array 750 | items: 751 | type: string 752 | responses: 753 | default: 754 | $ref: "#/components/responses/jsonResponse" 755 | 756 | /engines/{engine_name}/analytics/clicks: 757 | parameters: 758 | - $ref: "#/components/parameters/engineNameParam" 759 | get: 760 | operationId: getTopClicksAnalytics 761 | summary: Returns the number of clicks received by a document in descending order. 762 | externalDocs: 763 | url: https://swiftype.com/documentation/app-search/api/analytics/clicks 764 | tags: 765 | - Analytics API 766 | parameters: 767 | - name: query 768 | in: query 769 | description: Filter clicks over a search query. 770 | schema: 771 | type: string 772 | - $ref: "#/components/parameters/pageSize" 773 | - name: filters 774 | in: query 775 | description: Analytics filters 776 | schema: 777 | type: array 778 | items: 779 | type: object 780 | responses: 781 | default: 782 | $ref: "#/components/responses/jsonResponse" 783 | 784 | /engines/{engine_name}/analytics/queries: 785 | parameters: 786 | - $ref: "#/components/parameters/engineNameParam" 787 | get: 788 | operationId: getTopQueriesAnalytics 789 | summary: Returns queries anlaytics by usage count. 790 | externalDocs: 791 | url: https://swiftype.com/documentation/app-search/api/analytics/queries 792 | tags: 793 | - Analytics API 794 | parameters: 795 | - $ref: "#/components/parameters/pageSize" 796 | - name: filters 797 | in: query 798 | description: Analytics filters 799 | schema: 800 | type: array 801 | items: 802 | type: object 803 | responses: 804 | default: 805 | $ref: "#/components/responses/jsonResponse" 806 | 807 | /engines/{engine_name}/analytics/counts: 808 | parameters: 809 | - $ref: "#/components/parameters/engineNameParam" 810 | get: 811 | operationId: getCountAnalytics 812 | summary: Returns the number of clicks and total number of queries over a period. 813 | externalDocs: 814 | url: https://swiftype.com/documentation/app-search/api/analytics/counts 815 | tags: 816 | - Analytics API 817 | parameters: 818 | - name: filters 819 | in: query 820 | description: Analytics filters 821 | schema: 822 | type: array 823 | items: 824 | type: object 825 | - name: interval 826 | in: query 827 | description: You can define an interval along with your date range. Can be either hour or day. 828 | schema: 829 | type: string 830 | responses: 831 | default: 832 | $ref: "#/components/responses/jsonResponse" 833 | 834 | /engines/{engine_name}/logs/api: 835 | parameters: 836 | - $ref: "#/components/parameters/engineNameParam" 837 | get: 838 | operationId: getApiLogs 839 | summary: The API Log displays API request and response data at the Engine level. 840 | externalDocs: 841 | url: https://swiftype.com/documentation/app-search/api/logs 842 | tags: 843 | - Logs API 844 | parameters: 845 | - name: filters.date.from 846 | in: query 847 | description: Filter date from. 848 | required: true 849 | schema: 850 | type: string 851 | x-codegen-param-name: fromDate 852 | - name: filters.date.to 853 | in: query 854 | description: Filter date to. 855 | required: true 856 | schema: 857 | type: string 858 | x-codegen-param-name: toDate 859 | - $ref: "#/components/parameters/currentPage" 860 | - $ref: "#/components/parameters/pageSize" 861 | - name: query 862 | in: query 863 | description: Use this to specify a particular endpoint, like analytics, search, curations and so on. 864 | schema: 865 | type: string 866 | - name: filters.status 867 | in: query 868 | description: "Filter based on a particular status code: 400, 401, 403, 429, 200." 869 | schema: 870 | type: string 871 | x-codegen-param-name: httpStatusFilter 872 | - name: filters.method 873 | in: query 874 | description: "Filter based on a particular HTTP method: GET, POST, PUT, PATCH, DELETE." 875 | schema: 876 | type: string 877 | x-codegen-param-name: httpMethodFilter 878 | - name: sort_direction 879 | in: query 880 | description: "Would you like to have your results ascending, oldest to newest, or descending, newest to oldest?" 881 | schema: 882 | type: string 883 | responses: 884 | default: 885 | $ref: "#/components/responses/jsonResponse" 886 | -------------------------------------------------------------------------------- /resources/api/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "clientClass": "AbstractClient", 3 | "gitUserId": "elastic", 4 | "gitRepoId": "app-search-php", 5 | "artifactVersion": "1.0.0", 6 | "invokerPackage": "Elastic\\AppSearch\\Client", 7 | "helpUrl": "https://discuss.elastic.co/c/app-search", 8 | "copyright": "© [Elastic](https://github.com/elastic)" 9 | } 10 | -------------------------------------------------------------------------------- /resources/api/templates/README.mustache: -------------------------------------------------------------------------------- 1 | {{>readme_title}} 2 | {{>readme_content}} 3 | {{>readme_install}} 4 | {{>readme_usage}} 5 | ### Clients methods 6 | 7 | Method | Description | Documentation 8 | ------------|-------------|-------------- 9 | **`createEngine`**| Creates a new engine.

**Parameters :**
- `$name` (required)
- `$language`
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#create) 10 | **`createMetaEngine`**| Creates a new meta engine.

**Parameters :**
- `$name` (required)
- `$sourceEngines` (required)
|[Endpoint Documentation](https://swiftype.com/documentation/app-search/api/engines#create) 11 | {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#vendorExtensions.x-add-to-documentation}}**`{{operationId}}`**| {{#summary}}{{.}}{{/summary}}{{#allParams.0}}

**Parameters :**
{{#allParams}} - `${{paramName}}`{{#required}} (required) {{/required}}{{#hasMore}}
{{/hasMore}}{{/allParams}}{{/allParams.0}}
{{#externalDocs}}|[Endpoint Documentation]({{url}}){{/externalDocs}} 12 | {{/vendorExtensions.x-add-to-documentation}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} 13 | {{>readme_dev}} 14 | {{>readme_faq}} 15 | {{>readme_contrib}} 16 | {{>readme_licence}} 17 | -------------------------------------------------------------------------------- /resources/api/templates/header.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is part of the Elastic App Search PHP Client package. 3 | * 4 | * For the full copyright and license information, please view the LICENSE 5 | * file that was distributed with this source code. 6 | */ 7 | -------------------------------------------------------------------------------- /resources/api/templates/readme_install.mustache: -------------------------------------------------------------------------------- 1 | ## Getting started 🐣 2 | 3 | Using this client assumes that you have already an instance of Elastic App Search up and running. 4 | 5 | 6 | 7 | You can find more information about Elastic App Search at : https://www.elastic.co/app-search. 8 | 9 | You can install the client in your project by using composer: 10 | 11 | ```bash 12 | composer require elastic/app-search 13 | ``` 14 | 15 | ### Versioning 16 | 17 | This client is versioned and released alongside App Search. 18 | 19 | To guarantee compatibility, use the most recent version of this library within the major version of the corresponding App Search implementation. 20 | 21 | For example, for App Search `7.3`, use `7.3` of this library or above, but not `8.0`. 22 | 23 | If you are using the [SaaS version available on swiftype.com](https://app.swiftype.com/as) of App Search, you should use the version 7.5.x of the client. 24 | -------------------------------------------------------------------------------- /resources/api/templates/readme_title.mustache: -------------------------------------------------------------------------------- 1 |

Elastic App Search Logo

2 | 3 |

CircleCI buidl

4 | 5 | > A first-party PHP client for building excellent, relevant search experiences with [Elastic App Search](https://www.elastic.co/products/app-search). 6 | -------------------------------------------------------------------------------- /resources/api/templates/readme_usage.mustache: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ### Configuring the client 4 | 5 | #### Basic client instantiation 6 | 7 | To instantiate a new client you can use `\Elastic\AppSearch\Client\ClientBuilder`: 8 | 9 | ```php 10 | $apiEndpoint = 'http://localhost:3002/'; 11 | $apiKey = 'private-XXXXXXXXXXXX'; 12 | $clientBuilder = \Elastic\AppSearch\Client\ClientBuilder::create($apiEndpoint, $apiKey); 13 | 14 | $client = $clientBuilder->build(); 15 | ``` 16 | 17 | **Notes:** 18 | 19 | - The resulting client will be of type `\Elastic\AppSearch\Client\Client` 20 | 21 | - You can find the API endpoint and your API key URL in the credentials sections of the App Search dashboard. 22 | 23 | - You can use any type of API Key (private, public or admin). The client will throw an exception if you try to execute an action that is not authorized for the key used. 24 | 25 | ### Basic usage 26 | 27 | #### Retrieve or create an engine 28 | 29 | Most methods of the API require that you have access to an Engine. 30 | 31 | To check if an Engine exists and retrieve its configuration, you can use the `Client::getEngine` method : 32 | 33 | ```php 34 | $engine = $client->getEngine('my-engine'); 35 | ``` 36 | 37 | If the Engine does not exists yet, you can create it by using the `Client::createEngine` method : 38 | 39 | ```php 40 | $engine = $client->createEngine('my-engine', 'en'); 41 | ``` 42 | 43 | The second parameter (`$language`) is optional. Set it to `null` to apply the `universal` language. 44 | 45 | [Read more](https://swiftype.com/documentation/app-search/api/engines#multi-language) about language support. 46 | 47 | #### Index some documents 48 | 49 | You can use the `Client::indexDocuments` method to index some documents into the Engine: 50 | 51 | ```php 52 | $documents = [ 53 | ['id' => 'first-document', 'name' => 'Document name', 'description' => 'Document description'], 54 | ['id' => 'other-document', 'name' => 'Other document name', 'description' => 'Other description'], 55 | ]; 56 | 57 | $indexingResults = $client->indexDocuments('my-engine', $documents); 58 | ``` 59 | 60 | The `$indexingResults` array will contain the result of the indexation of each documents. You should always check the content of the result. 61 | 62 | [Read more](https://swiftype.com/documentation/app-search/api/documents#create) about document indexing. 63 | 64 | #### Search 65 | 66 | You can use the `Client::search` method to search in your Engine: 67 | 68 | ```php 69 | $searchParams = [ 70 | 'page' => ['current' => 1, 'size' => 10] 71 | ]; 72 | 73 | $searchResponse = $client->search('my-engine', 'search text', $searchParams); 74 | ``` 75 | If you want to match all documents you can use and empty search query `''` as second parameter (`$queryText`). 76 | 77 | The `$searchRequestParams` parameter is optional and can be used to use advanced search features. Allowed params are : 78 | 79 | Param name | Documentation URL 80 | --------------- | ---------------------------------------------------------------------- 81 | `page` | https://swiftype.com/documentation/app-search/api/search#paging 82 | `filters` | https://swiftype.com/documentation/app-search/api/search/filters 83 | `facets` | https://swiftype.com/documentation/app-search/api/search/facets 84 | `sort` | https://swiftype.com/documentation/app-search/api/search/sorting 85 | `boosts` | https://swiftype.com/documentation/app-search/api/search/boosts 86 | `search_fields` | https://swiftype.com/documentation/app-search/api/search/search-fields 87 | `result_fields` | https://swiftype.com/documentation/app-search/api/search/result-fields 88 | `group` | https://swiftype.com/documentation/app-search/api/search/grouping 89 | 90 | The search response will contains at least a meta field and a results field as shown in this example: 91 | 92 | ```php 93 | [ 94 | 'meta' => [ 95 | 'warnings' => [], 96 | 'page' => [ 97 | 'current' => 1, 98 | 'total_pages' => 1, 99 | 'total_results' => 1, 100 | 'size' => 10 101 | ], 102 | 'request_id' => 'feff7cf2359a6f6da84586969ef0ca89' 103 | ], 104 | 'results' => [ 105 | [ 106 | 'id' => ['raw' => 'first-document'], 107 | 'name' => ['raw' => 'Document name'], 108 | 'description' => ['raw' => ['Document description'] 109 | ] 110 | ] 111 | ] 112 | ] 113 | ``` 114 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /scripts/tests/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source .circleci/retrieve-credentials.sh 6 | 7 | echo "Running unit tests" 8 | vendor/bin/phpunit -c phpunit.xml.dist --testsuite unit 9 | 10 | echo "Running integration tests" 11 | vendor/bin/phpunit -c phpunit.xml.dist --testsuite integration 12 | --------------------------------------------------------------------------------