├── .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 |

13 |
14 | 
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 | 
2 |
3 | 
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 |
--------------------------------------------------------------------------------