Version Notes
Big update: Doofinder PHP client library.
Download this release
Release Info
Developer | Carlos Escribano Rey |
Extension | Doofinder_Feed |
Version | 1.8.0 |
Comparing to | |
See all releases |
Code changes from version 1.7.2 to 1.8.0
- app/code/community/Doofinder/Feed/Block/Adminhtml/Log/View.php +1 -1
- app/code/community/Doofinder/Feed/Block/Adminhtml/Map/Additional.php +1 -1
- app/code/community/Doofinder/Feed/Block/Integration.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Buttons/Generate.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Buttons/ViewLog.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Crondescription.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Datetime.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Description.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/File.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Layerdescription.php +1 -1
- app/code/community/Doofinder/Feed/Block/Settings/Panel/Message.php +1 -1
- app/code/community/Doofinder/Feed/Helper/Data.php +2 -2
- app/code/community/Doofinder/Feed/Helper/Log.php +2 -2
- app/code/community/Doofinder/Feed/Helper/Search.php +103 -15
- app/code/community/Doofinder/Feed/Helper/Tax.php +2 -2
- app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Backend/Cron.php +1 -1
- app/code/community/Doofinder/Feed/Model/Config.php +2 -2
- app/code/community/Doofinder/Feed/Model/Cron.php +1 -1
- app/code/community/Doofinder/Feed/Model/Generator.php +2 -2
- app/code/community/Doofinder/Feed/Model/Log.php +1 -1
- app/code/community/Doofinder/Feed/Model/Map/Product/Abstract.php +4 -6
- app/code/community/Doofinder/Feed/Model/Map/Product/Associated.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Bundle.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Configurable.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Downloadable.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Grouped.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Simple.php +2 -2
- app/code/community/Doofinder/Feed/Model/Map/Product/Virtual.php +2 -2
- app/code/community/Doofinder/Feed/Model/Mysql4/Cron.php +1 -1
- app/code/community/Doofinder/Feed/Model/Mysql4/Cron/Collection.php +1 -1
- app/code/community/Doofinder/Feed/Model/Mysql4/Log.php +1 -1
- app/code/community/Doofinder/Feed/Model/Mysql4/Log/Collection.php +1 -1
- app/code/community/Doofinder/Feed/Model/Observers/Feed.php +2 -16
- app/code/community/Doofinder/Feed/Model/Observers/Logs.php +1 -1
- app/code/community/Doofinder/Feed/Model/Observers/Schedule.php +1 -1
- app/code/community/Doofinder/Feed/Model/Resource/Mysql4/Setup.php +1 -1
- app/code/community/Doofinder/Feed/Model/System/Config/Backend/Map/Additional.php +1 -1
- app/code/community/Doofinder/Feed/Model/System/Config/Reset.php +1 -1
- app/code/community/Doofinder/Feed/Model/System/Config/Source/Product/Attributes.php +1 -1
- app/code/community/Doofinder/Feed/Model/Tools.php +2 -2
- app/code/community/Doofinder/Feed/controllers/DoofinderFeedFeedController.php +1 -1
- app/code/community/Doofinder/Feed/controllers/DoofinderFeedLogController.php +1 -1
- app/code/community/Doofinder/Feed/controllers/FeedController.php +2 -2
- app/code/community/Doofinder/Feed/controllers/IndexController.php +2 -2
- app/code/community/Doofinder/Feed/etc/config.xml +1 -1
- lib/Doofinder/doofinder_api.php +0 -804
- lib/Doofinder/doofinder_management_api.php +0 -408
- lib/Doofinder/errors.php +0 -48
- lib/php-doofinder/CHANGELOG.txt +38 -0
- lib/php-doofinder/README.md +599 -0
- lib/php-doofinder/autoload.php +21 -0
- lib/php-doofinder/composer.json +27 -0
- lib/php-doofinder/phpunit.xml +8 -0
- lib/php-doofinder/src/Management/AggregatesIterator.php +59 -0
- lib/php-doofinder/src/Management/Client.php +144 -0
- lib/php-doofinder/src/Management/Errors/BadRequest.php +5 -0
- lib/php-doofinder/src/Management/Errors/NotAllowed.php +5 -0
- lib/php-doofinder/src/Management/Errors/NotFound.php +4 -0
- lib/php-doofinder/src/Management/Errors/NotProcessedResponse.php +5 -0
- lib/php-doofinder/src/Management/Errors/QuotaExhausted.php +5 -0
- lib/php-doofinder/src/Management/Errors/ThrottledResponse.php +5 -0
- lib/php-doofinder/src/Management/Errors/Utils.php +53 -0
- lib/php-doofinder/src/Management/Errors/WrongResponse.php +5 -0
- lib/php-doofinder/src/Management/ItemsResultSet.php +60 -0
- lib/php-doofinder/src/Management/ScrollIterator.php +49 -0
- lib/php-doofinder/src/Management/SearchEngine.php +269 -0
- lib/php-doofinder/src/Management/TopTermsIterator.php +54 -0
- lib/php-doofinder/src/Search/Client.php +581 -0
- lib/php-doofinder/src/Search/Error.php +10 -0
- lib/php-doofinder/src/Search/Results.php +220 -0
- lib/php-doofinder/src/Test/Management/AggregatesIteratorTest.php +77 -0
- lib/php-doofinder/src/Test/Management/ClientTest.php +94 -0
- lib/php-doofinder/src/Test/Management/ScrollIteratorTest.php +67 -0
- lib/php-doofinder/src/Test/Management/SearchEngineTest.php +355 -0
- lib/php-doofinder/src/Test/Management/TopTermsIteratorTest.php +96 -0
- lib/php-doofinder/src/Test/Search/ClientTest.php +381 -0
- lib/php-doofinder/src/Test/Search/ResultsTests.php +94 -0
- package.xml +6 -6
app/code/community/Doofinder/Feed/Block/Adminhtml/Log/View.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Adminhtml_Log_View extends Mage_Adminhtml_Block_Widget_Grid
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Adminhtml_Log_View extends Mage_Adminhtml_Block_Widget_Grid
|
app/code/community/Doofinder/Feed/Block/Adminhtml/Map/Additional.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Adminhtml_Map_Additional extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Adminhtml_Map_Additional extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Integration.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Integration extends Mage_Core_Block_Abstract
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Integration extends Mage_Core_Block_Abstract
|
app/code/community/Doofinder/Feed/Block/Settings/Buttons/Generate.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Buttons_Generate extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Buttons_Generate extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Settings/Buttons/ViewLog.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Buttons_ViewLog extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Buttons_ViewLog extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Crondescription.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_CronDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_CronDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Datetime.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Datetime extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Datetime extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Description.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Description extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Description extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/File.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_File extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_File extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Layerdescription.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_LayerDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_LayerDescription extends Doofinder_Feed_Block_Settings_Panel_Description
|
app/code/community/Doofinder/Feed/Block/Settings/Panel/Message.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Message extends Mage_Adminhtml_Block_System_Config_Form_Field
|
6 |
/**
|
7 |
* @category blocks
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Block_Settings_Panel_Message extends Mage_Adminhtml_Block_System_Config_Form_Field
|
app/code/community/Doofinder/Feed/Helper/Data.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Data helper for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Data extends Mage_Core_Helper_Abstract
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Data helper for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Data extends Mage_Core_Helper_Abstract
|
app/code/community/Doofinder/Feed/Helper/Log.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Log helper for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Log extends Mage_Core_Helper_Abstract
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Log helper for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Log extends Mage_Core_Helper_Abstract
|
app/code/community/Doofinder/Feed/Helper/Search.php
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
<?php
|
2 |
-
require_once(Mage::getBaseDir('lib') . DS. 'Doofinder' . DS .'doofinder_api.php');
|
3 |
|
4 |
class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
5 |
{
|
@@ -9,6 +8,43 @@ class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
|
9 |
protected $_lastSearch = null;
|
10 |
protected $_lastResults = null;
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
/**
|
13 |
* Perform a doofinder search on given key.
|
14 |
*
|
@@ -20,31 +56,31 @@ class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
|
20 |
*/
|
21 |
public function performDoofinderSearch($queryText)
|
22 |
{
|
23 |
-
$hashId =
|
24 |
-
$apiKey =
|
25 |
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/request_limit', Mage::app()->getStore());
|
26 |
-
$ids = false;
|
27 |
|
28 |
-
$
|
29 |
-
$
|
|
|
30 |
|
31 |
// Store objects
|
32 |
-
$this->_lastSearch = $
|
33 |
-
$this->_lastResults = $
|
34 |
|
35 |
-
return $this->retrieveIds($
|
36 |
}
|
37 |
|
38 |
/**
|
39 |
* Retrieve ids from Doofinder Results
|
40 |
*
|
41 |
-
* @param
|
42 |
* @return array
|
43 |
*/
|
44 |
-
protected function retrieveIds(
|
45 |
{
|
46 |
-
$ids =
|
47 |
-
foreach($
|
48 |
$ids[] = $result['id'];
|
49 |
}
|
50 |
|
@@ -61,8 +97,8 @@ class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
|
61 |
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/total_limit', Mage::app()->getStore());
|
62 |
$ids = $this->retrieveIds($this->_lastResults);
|
63 |
|
64 |
-
while (count($ids) < $limit && ($
|
65 |
-
$ids = array_merge($ids, $this->retrieveIds($
|
66 |
}
|
67 |
|
68 |
return $ids;
|
@@ -77,4 +113,56 @@ class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
|
77 |
{
|
78 |
return $this->_lastResults->getProperty('total');
|
79 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
}
|
1 |
<?php
|
|
|
2 |
|
3 |
class Doofinder_Feed_Helper_Search extends Mage_Core_Helper_Abstract
|
4 |
{
|
8 |
protected $_lastSearch = null;
|
9 |
protected $_lastResults = null;
|
10 |
|
11 |
+
/**
|
12 |
+
* @var \Doofinder\Api\Management\SearchEngine[]
|
13 |
+
*/
|
14 |
+
protected $_searchEngines = null;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Load Doofinder PHP library
|
18 |
+
*/
|
19 |
+
protected function loadDoofinderLibrary()
|
20 |
+
{
|
21 |
+
spl_autoload_register(array($this, 'autoload'), true, true);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get api key
|
26 |
+
*
|
27 |
+
* @param string $storeCode
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
protected function getApiKey($storeCode = null)
|
31 |
+
{
|
32 |
+
$storeCode = $storeCode === null ? Mage::app()->getStore() : $storeCode;
|
33 |
+
return Mage::getStoreConfig('doofinder_search/internal_settings/api_key', $storeCode);
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get hash id
|
38 |
+
*
|
39 |
+
* @param string $storeCode
|
40 |
+
* @return string
|
41 |
+
*/
|
42 |
+
protected function getHashId($storeCode = null)
|
43 |
+
{
|
44 |
+
$storeCode = $storeCode === null ? Mage::app()->getStore() : $storeCode;
|
45 |
+
return Mage::getStoreConfig('doofinder_search/internal_settings/hash_id', $storeCode);
|
46 |
+
}
|
47 |
+
|
48 |
/**
|
49 |
* Perform a doofinder search on given key.
|
50 |
*
|
56 |
*/
|
57 |
public function performDoofinderSearch($queryText)
|
58 |
{
|
59 |
+
$hashId = $this->getHashId();
|
60 |
+
$apiKey = $this->getApiKey();
|
61 |
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/request_limit', Mage::app()->getStore());
|
|
|
62 |
|
63 |
+
$this->loadDoofinderLibrary();
|
64 |
+
$client = new \Doofinder\Api\Search\Client($hashId, $apiKey);
|
65 |
+
$results = $client->query($queryText, null, ['rpp' => $limit, 'transformer' => 'onlyid', 'filter' => []]);
|
66 |
|
67 |
// Store objects
|
68 |
+
$this->_lastSearch = $client;
|
69 |
+
$this->_lastResults = $results;
|
70 |
|
71 |
+
return $this->retrieveIds($results);
|
72 |
}
|
73 |
|
74 |
/**
|
75 |
* Retrieve ids from Doofinder Results
|
76 |
*
|
77 |
+
* @param \Doofinder\Api\Search\Results $results
|
78 |
* @return array
|
79 |
*/
|
80 |
+
protected function retrieveIds(\Doofinder\Api\Search\Results $results)
|
81 |
{
|
82 |
+
$ids = [];
|
83 |
+
foreach ($results->getResults() as $result) {
|
84 |
$ids[] = $result['id'];
|
85 |
}
|
86 |
|
97 |
$limit = Mage::getStoreConfig('doofinder_search/internal_settings/total_limit', Mage::app()->getStore());
|
98 |
$ids = $this->retrieveIds($this->_lastResults);
|
99 |
|
100 |
+
while (count($ids) < $limit && ($results = $this->_lastSearch->nextPage())) {
|
101 |
+
$ids = array_merge($ids, $this->retrieveIds($results));
|
102 |
}
|
103 |
|
104 |
return $ids;
|
113 |
{
|
114 |
return $this->_lastResults->getProperty('total');
|
115 |
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Get Doofinder Search Engine
|
119 |
+
*
|
120 |
+
* @param string $storeCode
|
121 |
+
* @return \Doofinder\Api\Management\SearchEngine
|
122 |
+
*/
|
123 |
+
public function getDoofinderSearchEngine($storeCode)
|
124 |
+
{
|
125 |
+
if ($this->_searchEngines === null) {
|
126 |
+
$this->_searchEngines = [];
|
127 |
+
|
128 |
+
// Create DoofinderManagementApi instance
|
129 |
+
$this->loadDoofinderLibrary();
|
130 |
+
$doofinderManagementApi = new \Doofinder\Api\Management\Client($this->getApiKey($storeCode));
|
131 |
+
|
132 |
+
foreach ($doofinderManagementApi->getSearchEngines() as $searchEngine) {
|
133 |
+
$this->_searchEngines[$searchEngine->hashid] = $searchEngine;
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
// Prepare SearchEngine instance
|
138 |
+
$hashId = $this->getHashId($storeCode);
|
139 |
+
if (!empty($this->_searchEngines[$hashId])) {
|
140 |
+
return $this->_searchEngines[$hashId];
|
141 |
+
}
|
142 |
+
|
143 |
+
return false;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Autoloader for 'php-doofinder' library
|
148 |
+
*/
|
149 |
+
protected function autoload($className)
|
150 |
+
{
|
151 |
+
$libraryPrefix = 'Doofinder\\Api\\';
|
152 |
+
$libraryDirectory = Mage::getBaseDir('lib') . DS. 'php-doofinder' . DS . 'src' . DS;
|
153 |
+
|
154 |
+
$len = strlen($libraryPrefix);
|
155 |
+
|
156 |
+
// Binary safe comparison of $len first characters
|
157 |
+
if (strncmp($libraryPrefix, $className, $len) !== 0) {
|
158 |
+
return;
|
159 |
+
}
|
160 |
+
|
161 |
+
$classPath = str_replace('\\', '/', substr($className, $len)) . '.php';
|
162 |
+
$file = $libraryDirectory . $classPath;
|
163 |
+
|
164 |
+
if (file_exists($file)) {
|
165 |
+
require $file;
|
166 |
+
}
|
167 |
+
}
|
168 |
}
|
app/code/community/Doofinder/Feed/Helper/Tax.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Tax helper for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Tax extends Mage_Tax_Helper_Data
|
6 |
/**
|
7 |
* @category Helpers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Tax helper for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Helper_Tax extends Mage_Tax_Helper_Data
|
app/code/community/Doofinder/Feed/Model/Adminhtml/System/Config/Backend/Cron.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Adminhtml_System_Config_Backend_Cron extends Mage_Core_Model_Config_Data {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Adminhtml_System_Config_Backend_Cron extends Mage_Core_Model_Config_Data {
|
app/code/community/Doofinder/Feed/Model/Config.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Config model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Config extends Mage_Core_Model_Config_Data
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Config model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Config extends Mage_Core_Model_Config_Data
|
app/code/community/Doofinder/Feed/Model/Cron.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Cron extends Mage_Core_Model_Abstract {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Cron extends Mage_Core_Model_Abstract {
|
app/code/community/Doofinder/Feed/Model/Generator.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Generator model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
if (!defined('DS'))
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Generator model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
if (!defined('DS'))
|
app/code/community/Doofinder/Feed/Model/Log.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Log extends Mage_Core_Model_Abstract {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Log extends Mage_Core_Model_Abstract {
|
app/code/community/Doofinder/Feed/Model/Map/Product/Abstract.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Abstract Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Abstract extends Varien_Object
|
@@ -309,8 +309,7 @@ class Doofinder_Feed_Model_Map_Product_Abstract extends Varien_Object
|
|
309 |
$stock_status = $defaultVal;
|
310 |
$stock_status = trim(strtolower($stock_status));
|
311 |
|
312 |
-
if (false === array_search($stock_status,
|
313 |
-
$this->getConfig()->getAllowedStockStatuses()))
|
314 |
$stock_status = $this->getConfig()->getOutOfStockStatus();
|
315 |
|
316 |
$fieldData = $stock_status;
|
@@ -340,8 +339,7 @@ class Doofinder_Feed_Model_Map_Product_Abstract extends Varien_Object
|
|
340 |
$defaultVal = isset($map['default_value']) ? $map['default_value'] : "";
|
341 |
$defaultVal = trim(strtolower($defaultVal));
|
342 |
|
343 |
-
if (false === array_search($defaultVal,
|
344 |
-
$this->getConfig()->getAllowedConditions()))
|
345 |
$defaultVal = $this->getConfig()->getConditionNew();
|
346 |
|
347 |
$fieldData = $defaultVal;
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Abstract Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Abstract extends Varien_Object
|
309 |
$stock_status = $defaultVal;
|
310 |
$stock_status = trim(strtolower($stock_status));
|
311 |
|
312 |
+
if (false === array_search($stock_status, (array) $this->getConfig()->getAllowedStockStatuses()))
|
|
|
313 |
$stock_status = $this->getConfig()->getOutOfStockStatus();
|
314 |
|
315 |
$fieldData = $stock_status;
|
339 |
$defaultVal = isset($map['default_value']) ? $map['default_value'] : "";
|
340 |
$defaultVal = trim(strtolower($defaultVal));
|
341 |
|
342 |
+
if (false === array_search($defaultVal, (array) $this->getConfig()->getAllowedConditions()))
|
|
|
343 |
$defaultVal = $this->getConfig()->getConditionNew();
|
344 |
|
345 |
$fieldData = $defaultVal;
|
app/code/community/Doofinder/Feed/Model/Map/Product/Associated.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Associated Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Associated
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Associated Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Associated
|
app/code/community/Doofinder/Feed/Model/Map/Product/Bundle.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Bundle Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Bundle
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Bundle Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Bundle
|
app/code/community/Doofinder/Feed/Model/Map/Product/Configurable.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Configurable Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Configurable
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Configurable Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Configurable
|
app/code/community/Doofinder/Feed/Model/Map/Product/Downloadable.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Downloadable Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Downloadable
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Downloadable Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Downloadable
|
app/code/community/Doofinder/Feed/Model/Map/Product/Grouped.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Grouped Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Grouped
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Grouped Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Grouped
|
app/code/community/Doofinder/Feed/Model/Map/Product/Simple.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Simple Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Simple
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Simple Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Simple
|
app/code/community/Doofinder/Feed/Model/Map/Product/Virtual.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Virtual Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Virtual
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Virtual Product Map Model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Map_Product_Virtual
|
app/code/community/Doofinder/Feed/Model/Mysql4/Cron.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Cron extends Mage_Core_Model_Mysql4_Abstract {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Cron extends Mage_Core_Model_Mysql4_Abstract {
|
app/code/community/Doofinder/Feed/Model/Mysql4/Cron/Collection.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Cron_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Cron_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract {
|
app/code/community/Doofinder/Feed/Model/Mysql4/Log.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Log extends Mage_Core_Model_Mysql4_Abstract {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Log extends Mage_Core_Model_Mysql4_Abstract {
|
app/code/community/Doofinder/Feed/Model/Mysql4/Log/Collection.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Log_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Mysql4_Log_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
|
app/code/community/Doofinder/Feed/Model/Observers/Feed.php
CHANGED
@@ -6,11 +6,9 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
-
require_once(Mage::getBaseDir('lib') . DS. 'Doofinder' . DS .'doofinder_management_api.php');
|
13 |
-
|
14 |
class Doofinder_Feed_Model_Observers_Feed
|
15 |
{
|
16 |
|
@@ -55,18 +53,6 @@ class Doofinder_Feed_Model_Observers_Feed
|
|
55 |
// Terminate updates where there is no store enabled
|
56 |
if (empty($storeCodes)) return;
|
57 |
|
58 |
-
// Get search engines
|
59 |
-
$apiKey = Mage::getStoreConfig('doofinder_search/internal_settings/api_key', Mage::app()->getStore());
|
60 |
-
$dma = new DoofinderManagementApi($apiKey);
|
61 |
-
$searchEngines = $dma->getSearchEngines();
|
62 |
-
|
63 |
-
// Set engines array key as hashid
|
64 |
-
foreach ($searchEngines as $key => $searchEngine) {
|
65 |
-
$searchEngines[$searchEngine->hashid] = $searchEngine;
|
66 |
-
unset($searchEngines[$key]);
|
67 |
-
}
|
68 |
-
|
69 |
-
|
70 |
// Loop over all stores and update relevant search engines
|
71 |
foreach ($storeCodes as $storeCode) {
|
72 |
// Set store code
|
@@ -107,7 +93,7 @@ class Doofinder_Feed_Model_Observers_Feed
|
|
107 |
continue;
|
108 |
}
|
109 |
|
110 |
-
$searchEngine = $
|
111 |
|
112 |
// Check if search engine exists and skip foreach iteration if not.
|
113 |
if (!$searchEngine) {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
|
|
|
|
12 |
class Doofinder_Feed_Model_Observers_Feed
|
13 |
{
|
14 |
|
53 |
// Terminate updates where there is no store enabled
|
54 |
if (empty($storeCodes)) return;
|
55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
// Loop over all stores and update relevant search engines
|
57 |
foreach ($storeCodes as $storeCode) {
|
58 |
// Set store code
|
93 |
continue;
|
94 |
}
|
95 |
|
96 |
+
$searchEngine = Mage::helper('doofinder_feed/search')->getDoofinderSearchEngine($this->storeCode);
|
97 |
|
98 |
// Check if search engine exists and skip foreach iteration if not.
|
99 |
if (!$searchEngine) {
|
app/code/community/Doofinder/Feed/Model/Observers/Logs.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Observers_Logs
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Observers_Logs
|
app/code/community/Doofinder/Feed/Model/Observers/Schedule.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Observers_Schedule
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Observers_Schedule
|
app/code/community/Doofinder/Feed/Model/Resource/Mysql4/Setup.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Resource_Mysql4_Setup extends Mage_Core_Model_Resource_Setup {
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_Resource_Mysql4_Setup extends Mage_Core_Model_Resource_Setup {
|
app/code/community/Doofinder/Feed/Model/System/Config/Backend/Map/Additional.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Backend_Map_Additional extends Mage_Adminhtml_Model_System_Config_Backend_Serialized
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Backend_Map_Additional extends Mage_Adminhtml_Model_System_Config_Backend_Serialized
|
app/code/community/Doofinder/Feed/Model/System/Config/Reset.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Reset extends Mage_Core_Model_Config_Data
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Reset extends Mage_Core_Model_Config_Data
|
app/code/community/Doofinder/Feed/Model/System/Config/Source/Product/Attributes.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Source_Product_Attributes
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_Model_System_Config_Source_Product_Attributes
|
app/code/community/Doofinder/Feed/Model/Tools.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Tools model for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Tools extends Varien_Object
|
6 |
/**
|
7 |
* @category Models
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Tools model for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_Model_Tools extends Varien_Object
|
app/code/community/Doofinder/Feed/controllers/DoofinderFeedFeedController.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_DoofinderFeedFeedController extends Mage_Adminhtml_Controller_Action
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_DoofinderFeedFeedController extends Mage_Adminhtml_Controller_Action
|
app/code/community/Doofinder/Feed/controllers/DoofinderFeedLogController.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_DoofinderFeedLogController extends Mage_Adminhtml_Controller_Action
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
class Doofinder_Feed_DoofinderFeedLogController extends Mage_Adminhtml_Controller_Action
|
app/code/community/Doofinder/Feed/controllers/FeedController.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Feed controller for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_FeedController extends Mage_Core_Controller_Front_Action
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Feed controller for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_FeedController extends Mage_Core_Controller_Front_Action
|
app/code/community/Doofinder/Feed/controllers/IndexController.php
CHANGED
@@ -6,13 +6,13 @@
|
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
-
* @version 1.
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Index controller for Doofinder Feed
|
14 |
*
|
15 |
-
* @version 1.
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_IndexController extends Mage_Core_Controller_Front_Action
|
6 |
/**
|
7 |
* @category controllers
|
8 |
* @package Doofinder_Feed
|
9 |
+
* @version 1.8.0
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
* Index controller for Doofinder Feed
|
14 |
*
|
15 |
+
* @version 1.8.0
|
16 |
* @package Doofinder_Feed
|
17 |
*/
|
18 |
class Doofinder_Feed_IndexController extends Mage_Core_Controller_Front_Action
|
app/code/community/Doofinder/Feed/etc/config.xml
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
|
4 |
<modules>
|
5 |
<Doofinder_Feed>
|
6 |
-
<version>1.
|
7 |
</Doofinder_Feed>
|
8 |
</modules>
|
9 |
<global>
|
3 |
|
4 |
<modules>
|
5 |
<Doofinder_Feed>
|
6 |
+
<version>1.8.0</version>
|
7 |
</Doofinder_Feed>
|
8 |
</modules>
|
9 |
<global>
|
lib/Doofinder/doofinder_api.php
DELETED
@@ -1,804 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Author:: JoeZ99 (<jzarate@gmail.com>). all credit to Gilles Devaux (<gilles.devaux@gmail.com>) (https://github.com/flaptor/indextank-php)
|
4 |
-
*
|
5 |
-
* License:: Apache License, Version 2.0
|
6 |
-
*
|
7 |
-
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
8 |
-
* not use this file except in compliance with the License. You may obtain
|
9 |
-
* a copy of the License at
|
10 |
-
*
|
11 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
12 |
-
*
|
13 |
-
* Unless required by applicable law or agreed to in writing, software
|
14 |
-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15 |
-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16 |
-
* License for the specific language governing permissions and limitations
|
17 |
-
* under the License.
|
18 |
-
*/
|
19 |
-
|
20 |
-
|
21 |
-
class DoofinderApi{
|
22 |
-
/*
|
23 |
-
* Basic client for an account.
|
24 |
-
* It needs an API url to be constructed.
|
25 |
-
* Its only method is to query the doofinder search server
|
26 |
-
* Returns a DoofinderResults object
|
27 |
-
*/
|
28 |
-
|
29 |
-
const URL_SUFFIX = '-search.doofinder.com';
|
30 |
-
const DEFAULT_TIMEOUT = 10000;
|
31 |
-
const DEFAULT_RPP = 10;
|
32 |
-
const DEFAULT_PARAMS_PREFIX = 'dfParam_';
|
33 |
-
const DEFAULT_API_VERSION = '4';
|
34 |
-
const VERSION = '5.2.5';
|
35 |
-
|
36 |
-
private $api_key = null; // user API_KEY
|
37 |
-
private $hashid = null; // hashid of the doofinder account
|
38 |
-
|
39 |
-
private $apiVersion = null;
|
40 |
-
private $url = null;
|
41 |
-
private $results = null;
|
42 |
-
private $query = null;
|
43 |
-
private $search_options = array(); // assoc. array with doofinder options to be sent as request parameters
|
44 |
-
|
45 |
-
private $page = 1; // the page of the search results we're at
|
46 |
-
private $queryName = null; // the name of the last successfull query made
|
47 |
-
private $lastQuery = null; // the last successfull query made
|
48 |
-
private $total = null; // total number of results obtained
|
49 |
-
private $maxScore = null;
|
50 |
-
private $paramsPrefix = self::DEFAULT_PARAMS_PREFIX;
|
51 |
-
private $serializationArray = null;
|
52 |
-
private $queryParameter = 'query'; // the parameter used for querying
|
53 |
-
private $allowedParameters = array('page', 'rpp', 'timeout', 'types', 'filter', 'query_name', 'transformer'); // request parameters that doofinder handle
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Constructor. account's hashid and api version set here
|
57 |
-
*
|
58 |
-
* @param string $hashid the account's hashid
|
59 |
-
* @param boolean $fromParams if set, the object is unserialized from GET or POST params
|
60 |
-
* @param array $init_options. associative array with some options:
|
61 |
-
* -'prefix' (default: 'dfParam_')=> the prefix to use when serializing.
|
62 |
-
* -'queryParameter' (default: 'query') => the parameter used for querying
|
63 |
-
* -'apiVersion' (default: '4')=> the api of the search server to query
|
64 |
-
* -'restrictedRequest'(default: $_REQUEST): =>restrict request object
|
65 |
-
* to look for params when unserializing. either 'get' or 'post'
|
66 |
-
* @throws DoofinderException if $hashid is not a md5 hash or api is no 4, 3.0 or 1.0
|
67 |
-
*/
|
68 |
-
function __construct($hashid, $api_key, $fromParams=false, $init_options = array()){
|
69 |
-
$zone_key_array = explode('-', $api_key);
|
70 |
-
|
71 |
-
if(2 === count($zone_key_array)){
|
72 |
-
$this->api_key = $zone_key_array[1];
|
73 |
-
$this->zone = $zone_key_array[0];
|
74 |
-
$this->url = "https://" . $this->zone . self::URL_SUFFIX;
|
75 |
-
} else {
|
76 |
-
throw new DoofinderException("API Key is no properly set.");
|
77 |
-
}
|
78 |
-
|
79 |
-
if(array_key_exists('prefix', $init_options)){
|
80 |
-
$this->paramsPrefix = $init_options['prefix'];
|
81 |
-
}
|
82 |
-
|
83 |
-
|
84 |
-
$this->allowedParameters = array_map(array($this, 'addprefix'), $this->allowedParameters);
|
85 |
-
|
86 |
-
|
87 |
-
if(array_key_exists('queryParameter', $init_options)){
|
88 |
-
$this->queryParameter = $init_options['queryParameter'];
|
89 |
-
} else {
|
90 |
-
$this->queryParameter = $this->paramsPrefix.$this->queryParameter;
|
91 |
-
}
|
92 |
-
|
93 |
-
|
94 |
-
$this->apiVersion = array_key_exists('apiVersion', $init_options) ?
|
95 |
-
$init_options['apiVersion'] : self::DEFAULT_API_VERSION;
|
96 |
-
$this->serializationArray = $_REQUEST;
|
97 |
-
if(array_key_exists('restrictedRequest', $init_options))
|
98 |
-
{
|
99 |
-
switch(strtolower($init_options['restrictedRequest'])){
|
100 |
-
case 'get':
|
101 |
-
$this->serializationArray = $_GET;
|
102 |
-
break;
|
103 |
-
case 'post':
|
104 |
-
$this->serializationArray = $_POST;
|
105 |
-
break;
|
106 |
-
}
|
107 |
-
}
|
108 |
-
$patt = '/^[0-9a-f]{32}$/i';
|
109 |
-
if(!preg_match($patt, $hashid))
|
110 |
-
{
|
111 |
-
throw new DoofinderException("Wrong hashid");
|
112 |
-
}
|
113 |
-
if(!in_array($this->apiVersion, array('5', '4', '3.0','1.0')))
|
114 |
-
{
|
115 |
-
throw new DoofinderException('Wrong API');
|
116 |
-
}
|
117 |
-
$this->hashid = $hashid;
|
118 |
-
if($fromParams)
|
119 |
-
{
|
120 |
-
$this->fromQuerystring();
|
121 |
-
}
|
122 |
-
|
123 |
-
}
|
124 |
-
|
125 |
-
private function addprefix($value){
|
126 |
-
return $this->paramsPrefix.$value;
|
127 |
-
}
|
128 |
-
|
129 |
-
/*
|
130 |
-
* translateFilter
|
131 |
-
*
|
132 |
-
* translates a range filter to the new ES format
|
133 |
-
* 'from'=>9, 'to'=>20 to 'gte'=>9, 'lte'=>20
|
134 |
-
*
|
135 |
-
* @param array $filter
|
136 |
-
* @return array the translated filter
|
137 |
-
*/
|
138 |
-
private function translateFilter($filter){
|
139 |
-
$new_filter = array();
|
140 |
-
foreach($filter as $key => $value){
|
141 |
-
if ($key === 'from') {
|
142 |
-
$new_filter['gte'] = $value;
|
143 |
-
} else if ($key === 'to') {
|
144 |
-
$new_filter['lte'] = $value;
|
145 |
-
} else {
|
146 |
-
$new_filter[$key] = $value;
|
147 |
-
}
|
148 |
-
}
|
149 |
-
return $new_filter;
|
150 |
-
}
|
151 |
-
|
152 |
-
private function reqHeaders(){
|
153 |
-
$headers = array();
|
154 |
-
$headers[] = 'Expect:'; //Fixes the HTTP/1.1 417 Expectation Failed
|
155 |
-
$authHeaderName = $this->apiVersion == '4' ? 'API Token: ' : 'authorization: ';
|
156 |
-
$headers[] = $authHeaderName . $this->api_key; //API Authorization
|
157 |
-
return $headers;
|
158 |
-
}
|
159 |
-
|
160 |
-
private function apiCall($entry_point='search', $params=array()){
|
161 |
-
$params['hashid'] = $this->hashid;
|
162 |
-
$args = http_build_query($this->sanitize($params)); // remove any null value from the array
|
163 |
-
|
164 |
-
$url = $this->url.'/'.$this->apiVersion.'/'.$entry_point.'?'.$args;
|
165 |
-
|
166 |
-
$session = curl_init($url);
|
167 |
-
curl_setopt($session, CURLOPT_CUSTOMREQUEST, 'GET');
|
168 |
-
curl_setopt($session, CURLOPT_HEADER, false); // Tell curl not to return headers
|
169 |
-
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Tell curl to return the response
|
170 |
-
curl_setopt($session, CURLOPT_HTTPHEADER, $this->reqHeaders()); // Adding request headers
|
171 |
-
$response = curl_exec($session);
|
172 |
-
$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
173 |
-
curl_close($session);
|
174 |
-
|
175 |
-
if (floor($httpCode / 100) == 2) {
|
176 |
-
return $response;
|
177 |
-
}
|
178 |
-
throw new DoofinderException($httpCode.' - '.$response, $httpCode);
|
179 |
-
}
|
180 |
-
|
181 |
-
public function getOptions(){
|
182 |
-
return $this->apiCall('options/'.$this->hashid);
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* query. makes the query to the doofinder search server.
|
187 |
-
* also set several search parameters through it's $options argument
|
188 |
-
*
|
189 |
-
* @param string $query the search query
|
190 |
-
* @param int $page the page number or the results to show
|
191 |
-
* @param array $options query options:
|
192 |
-
* - 'rpp'=> number of results per page. default 10
|
193 |
-
* - 'timeout' => timeout after which the search server drops the conn.
|
194 |
-
* defaults to 10 seconds
|
195 |
-
* - 'types' => types of index to search at. default: all.
|
196 |
-
* - 'filter' => filter to apply. ['color'=>['red','blue'], 'price'=>['from'=>33]]
|
197 |
-
* - any other param will be sent as a request parameter
|
198 |
-
* @return DoofinderResults results
|
199 |
-
*/
|
200 |
-
public function query($query=null, $page=null, $options = array()){
|
201 |
-
if($query){
|
202 |
-
$this->search_options['query'] = $query;
|
203 |
-
}
|
204 |
-
if($page){
|
205 |
-
$this->search_options['page'] = (int)$page;
|
206 |
-
}
|
207 |
-
foreach($options as $optionName => $optionValue){
|
208 |
-
$this->search_options[$optionName] = $options[$optionName];
|
209 |
-
}
|
210 |
-
|
211 |
-
$params = $this->search_options;
|
212 |
-
|
213 |
-
// translate filters
|
214 |
-
if(!empty($params['filter']))
|
215 |
-
{
|
216 |
-
foreach($params['filter'] as $filterName => $filterValue){
|
217 |
-
$params['filter'][$filterName] = $this->translateFilter($filterValue);
|
218 |
-
}
|
219 |
-
}
|
220 |
-
|
221 |
-
// no query? then match all documents
|
222 |
-
if(!$this->optionExists('query') || !trim($this->search_options['query'])){
|
223 |
-
$params['query_name'] = 'match_all';
|
224 |
-
}
|
225 |
-
|
226 |
-
// if filters without query_name, pre-query first to obtain it.
|
227 |
-
if (empty($params['query_name']) && !empty($params['filter']))
|
228 |
-
{
|
229 |
-
$filter = $params['filter'];
|
230 |
-
unset($params['filter']);
|
231 |
-
$dfResults = new DoofinderResults($this->apiCall('search', $params));
|
232 |
-
$params['query_name'] = $dfResults->getProperty('query_name');
|
233 |
-
$params['filter'] = $filter;
|
234 |
-
}
|
235 |
-
$dfResults = new DoofinderResults($this->apiCall('search', $params));
|
236 |
-
$this->page = $dfResults->getProperty('page');
|
237 |
-
$this->total = $dfResults->getProperty('total');
|
238 |
-
$this->search_options['query'] = $dfResults->getProperty('query');
|
239 |
-
$this->maxScore = $dfResults->getProperty('max_score');
|
240 |
-
$this->queryName = $dfResults->getProperty('query_name');
|
241 |
-
$this->lastQuery = $dfResults->getProperty('query');
|
242 |
-
|
243 |
-
return $dfResults;
|
244 |
-
}
|
245 |
-
|
246 |
-
/**
|
247 |
-
* hasNext
|
248 |
-
*
|
249 |
-
* @return boolean true if there is another page of results
|
250 |
-
*/
|
251 |
-
public function hasNext(){
|
252 |
-
return $this->page*$this->getRpp() < $this->total;
|
253 |
-
}
|
254 |
-
|
255 |
-
/**
|
256 |
-
* hasPrev
|
257 |
-
*
|
258 |
-
* @return true if there is a previous page of results
|
259 |
-
*/
|
260 |
-
public function hasPrev(){
|
261 |
-
return ($this->page-1)*$this->getRpp() > 0;
|
262 |
-
}
|
263 |
-
|
264 |
-
|
265 |
-
/**
|
266 |
-
* getPage
|
267 |
-
*
|
268 |
-
* obtain the current page number
|
269 |
-
* @return int the page number
|
270 |
-
*/
|
271 |
-
public function getPage(){
|
272 |
-
return $this->page;
|
273 |
-
}
|
274 |
-
|
275 |
-
/**
|
276 |
-
* setFilter
|
277 |
-
*
|
278 |
-
* set a filter for the query
|
279 |
-
* @param string filterName the name of the filter to set
|
280 |
-
* @param array filter if simple array, terms filter assumed
|
281 |
-
* if 'from', 'to' in keys, range filter assumed
|
282 |
-
*/
|
283 |
-
public function setFilter($filterName, $filter){
|
284 |
-
if(!$this->optionExists('filter')){
|
285 |
-
$this->search_options['filter'] = array();
|
286 |
-
}
|
287 |
-
$this->search_options['filter'][$filterName] = $filter;
|
288 |
-
}
|
289 |
-
|
290 |
-
/**
|
291 |
-
* getFilter
|
292 |
-
*
|
293 |
-
* get conditions for certain filter
|
294 |
-
* @param string filterName
|
295 |
-
* @return array filter conditions: - simple array if terms filter
|
296 |
-
* - 'from', 'to' assoc array if range f.
|
297 |
-
* @return false if no filter definition found
|
298 |
-
*/
|
299 |
-
public function getFilter($filterName){
|
300 |
-
if($this->optionExists('filter') && isset($this->search_options['filter'][$filterName])){
|
301 |
-
return $this->filter[$filterName];
|
302 |
-
}
|
303 |
-
return false;
|
304 |
-
}
|
305 |
-
|
306 |
-
/**
|
307 |
-
* getFilters
|
308 |
-
*
|
309 |
-
* get all filters and their configs
|
310 |
-
* @return array assoc array filterName => filterConditions
|
311 |
-
*/
|
312 |
-
public function getFilters(){
|
313 |
-
return $this->search_options['filter'];
|
314 |
-
}
|
315 |
-
|
316 |
-
|
317 |
-
/**
|
318 |
-
* addTerm
|
319 |
-
*
|
320 |
-
* add a term to a terms filter
|
321 |
-
* @param string filterName the filter to add the term to
|
322 |
-
* @param string term the term to add
|
323 |
-
*/
|
324 |
-
public function addTerm($filterName, $term){
|
325 |
-
if(!$this->optionExists('filter')){
|
326 |
-
$this->search_options['filter'] = array($filterName => array());
|
327 |
-
}
|
328 |
-
if(!isset($this->search_options['filter'][$filterName]))
|
329 |
-
{
|
330 |
-
$this->filter[$filterName] = array();
|
331 |
-
$this->search_options['filter'][$filterName] = array();
|
332 |
-
}
|
333 |
-
$this->filter[$filterName][] = $term;
|
334 |
-
$this->search_options['filter'][$filterName][] = $term;
|
335 |
-
}
|
336 |
-
|
337 |
-
/**
|
338 |
-
* removeTerm
|
339 |
-
*
|
340 |
-
* remove a term from a terms filter
|
341 |
-
* @param string filterName the filter to remove the term from
|
342 |
-
* @param string term the term to be removed
|
343 |
-
*/
|
344 |
-
public function removeTerm($filterName, $term){
|
345 |
-
if($this->optionExists('filter') && isset($this->search_options['filter'][$filterName]) &&
|
346 |
-
in_array($term, $this->search_options['filter'][$filterName]))
|
347 |
-
{
|
348 |
-
function filter_me($value){
|
349 |
-
global $term;
|
350 |
-
return $value != $term;
|
351 |
-
}
|
352 |
-
$this->search_options['filter'][$filterName] =
|
353 |
-
array_filter($this->search_options['filter'][$filterName], 'filter_me');
|
354 |
-
}
|
355 |
-
}
|
356 |
-
|
357 |
-
/**
|
358 |
-
* setRange
|
359 |
-
*
|
360 |
-
* set a range filter
|
361 |
-
* @param string filterName the filter to set
|
362 |
-
* @param int from the lower bound value. included
|
363 |
-
* @param int to the upper bound value. included
|
364 |
-
*/
|
365 |
-
public function setRange($filterName, $from=null, $to=null){
|
366 |
-
if(!$this->optionExists('filter')){
|
367 |
-
$this->search_options['filter'] = array($filterName=>array());
|
368 |
-
}
|
369 |
-
if(!isset($this->search_options['filter'][$filterName]))
|
370 |
-
{
|
371 |
-
$this->search_options['filter'][$filterName] = array();
|
372 |
-
}
|
373 |
-
if($from)
|
374 |
-
{
|
375 |
-
$this->search_options['filter'][$filterName]['from'] = $from;
|
376 |
-
}
|
377 |
-
if($to)
|
378 |
-
{
|
379 |
-
$this->search_options['filter'][$filterName]['to'] = $from;
|
380 |
-
}
|
381 |
-
}
|
382 |
-
|
383 |
-
/**
|
384 |
-
* toQuerystring
|
385 |
-
*
|
386 |
-
* 'serialize' the object's state to querystring params
|
387 |
-
* @param int $page the pagenumber. defaults to the current page
|
388 |
-
*/
|
389 |
-
public function toQuerystring($page=null){
|
390 |
-
|
391 |
-
foreach($this->search_options as $paramName => $paramValue){
|
392 |
-
if($paramName == 'query'){
|
393 |
-
$toParams[$this->queryParameter] = $paramValue;
|
394 |
-
} else {
|
395 |
-
$toParams[$this->paramsPrefix.$paramName] = $paramValue;
|
396 |
-
}
|
397 |
-
}
|
398 |
-
if($page){
|
399 |
-
$toParams[$this->paramsPrefix.'page'] = $page;
|
400 |
-
}
|
401 |
-
return http_build_query($toParams);
|
402 |
-
}
|
403 |
-
|
404 |
-
/**
|
405 |
-
* fromQuerystring
|
406 |
-
*
|
407 |
-
* obtain object's state from querystring params
|
408 |
-
* @param string $params where to obtain params from:
|
409 |
-
* - 'GET' $_GET params (default)
|
410 |
-
* - 'POST' $_POST params
|
411 |
-
*/
|
412 |
-
public function fromQuerystring(){
|
413 |
-
$doofinderReqParams = array_filter(array_keys($this->serializationArray),
|
414 |
-
array($this, 'belongsToDoofinder'));
|
415 |
-
|
416 |
-
foreach($doofinderReqParams as $dfReqParam){
|
417 |
-
if($dfReqParam == $this->queryParameter){
|
418 |
-
$keey = 'query';
|
419 |
-
} else {
|
420 |
-
$keey = substr($dfReqParam, strlen($this->paramsPrefix));
|
421 |
-
}
|
422 |
-
$this->search_options[$keey] = $this->serializationArray[$dfReqParam];
|
423 |
-
}
|
424 |
-
}
|
425 |
-
|
426 |
-
/**
|
427 |
-
* sanitize
|
428 |
-
*
|
429 |
-
* Clean array of keys with empty values
|
430 |
-
*
|
431 |
-
* @param array $params array to be cleaned
|
432 |
-
* @return array array with no empty keys
|
433 |
-
*/
|
434 |
-
private function sanitize($params){
|
435 |
-
$result = array();
|
436 |
-
foreach($params as $name => $value){
|
437 |
-
if (is_array($value)){
|
438 |
-
$result[$name] = $this->sanitize($value);
|
439 |
-
} else if (trim($value)){
|
440 |
-
$result[$name] = $value;
|
441 |
-
}
|
442 |
-
}
|
443 |
-
return $result;
|
444 |
-
}
|
445 |
-
|
446 |
-
/**
|
447 |
-
* belongsToDoofinder
|
448 |
-
*
|
449 |
-
* to know if certain parameter name belongs to doofinder serialization parameters
|
450 |
-
*
|
451 |
-
* @param string $paramName name of the param
|
452 |
-
* @return boolean true or false.
|
453 |
-
*/
|
454 |
-
private function belongsToDoofinder($paramName){
|
455 |
-
if($pos = strpos($paramName, '[')){
|
456 |
-
$paramName = substr($paramName, 0, $pos);
|
457 |
-
}
|
458 |
-
return in_array($paramName, $this->allowedParameters) || $paramName == $this->queryParameter;
|
459 |
-
}
|
460 |
-
|
461 |
-
/**
|
462 |
-
* optionExists
|
463 |
-
*
|
464 |
-
* checks whether a search option is defined in $this->search_options
|
465 |
-
*
|
466 |
-
* @param string $optionName
|
467 |
-
* @return boolean
|
468 |
-
*/
|
469 |
-
private function optionExists($optionName){
|
470 |
-
return array_key_exists($optionName, $this->search_options);
|
471 |
-
}
|
472 |
-
|
473 |
-
/**
|
474 |
-
* nextPage
|
475 |
-
*
|
476 |
-
* obtain the results for the next page
|
477 |
-
* @return DoofinderResults if there are results.
|
478 |
-
* @return null otherwise
|
479 |
-
*/
|
480 |
-
public function nextPage(){
|
481 |
-
if($this->hasNext())
|
482 |
-
{
|
483 |
-
return $this->query($this->lastQuery, $this->page+1 );
|
484 |
-
}
|
485 |
-
return null;
|
486 |
-
}
|
487 |
-
|
488 |
-
|
489 |
-
/**
|
490 |
-
* prevPage
|
491 |
-
*
|
492 |
-
* obtain results for the previous page
|
493 |
-
* @return DoofinderResults
|
494 |
-
* @return null otherwise
|
495 |
-
*/
|
496 |
-
public function prevPage(){
|
497 |
-
if($this->hasPrev())
|
498 |
-
{
|
499 |
-
return $this->query($this->lastQuery, $this->page-1 );
|
500 |
-
}
|
501 |
-
return null;
|
502 |
-
}
|
503 |
-
|
504 |
-
/**
|
505 |
-
* numPages
|
506 |
-
*
|
507 |
-
* @return integer the number of pages
|
508 |
-
*/
|
509 |
-
public function numPages(){
|
510 |
-
return ceil($this->total / $this->getRpp());
|
511 |
-
}
|
512 |
-
|
513 |
-
public function getRpp(){
|
514 |
-
$rpp = $this->optionExists('rpp') ? $this->search_options['rpp'] : null;
|
515 |
-
$rpp = $rpp ? $rpp: self::DEFAULT_RPP;
|
516 |
-
return $rpp;
|
517 |
-
}
|
518 |
-
/**
|
519 |
-
* setApiVersion
|
520 |
-
*
|
521 |
-
* sets the api version to use.
|
522 |
-
* @param string $apiVersion the api version , '1.0' or '3.0' or '4'
|
523 |
-
*/
|
524 |
-
public function setApiVersion($apiVersion){
|
525 |
-
$this->apiVersion = $apiVersion;
|
526 |
-
}
|
527 |
-
|
528 |
-
/**
|
529 |
-
* setPrefix
|
530 |
-
*
|
531 |
-
* sets the prefix that will be used for serialization to querystring params
|
532 |
-
* @param string $prefix the prefix
|
533 |
-
*/
|
534 |
-
public function setPrefix($prefix){
|
535 |
-
$this->paramsPrefix = $prefix;
|
536 |
-
}
|
537 |
-
|
538 |
-
/**
|
539 |
-
* setQueryName
|
540 |
-
*
|
541 |
-
* sets query_name
|
542 |
-
* CAUTION: node will complain if this is wrong
|
543 |
-
*/
|
544 |
-
public function setQueryName($queryName){
|
545 |
-
$this->queryName = $queryName;
|
546 |
-
}
|
547 |
-
|
548 |
-
/**
|
549 |
-
* getFilterType
|
550 |
-
* obtain the filter type (i.e. 'terms' or 'numeric range' from its conditions)
|
551 |
-
* @param array filter conditions
|
552 |
-
* @return string 'terms' or 'numericrange' false otherwise
|
553 |
-
*/
|
554 |
-
private function getFilterType($filter){
|
555 |
-
if(!is_array($filter))
|
556 |
-
{
|
557 |
-
return false;
|
558 |
-
}
|
559 |
-
if(count(array_intersect(array('from', 'to'), array_keys($filter)))>0)
|
560 |
-
{
|
561 |
-
return 'numericrange';
|
562 |
-
}
|
563 |
-
return 'terms';
|
564 |
-
}
|
565 |
-
|
566 |
-
|
567 |
-
}
|
568 |
-
|
569 |
-
/**
|
570 |
-
* @author JoeZ99 <jzarate@gmail.com>
|
571 |
-
*
|
572 |
-
* DoofinderResults
|
573 |
-
*
|
574 |
-
* Very thin wrapper of the results obtained from the doofinder server
|
575 |
-
* it holds to accessor:
|
576 |
-
* - getProperty : get single property of the search results (rpp, page, etc....)
|
577 |
-
* - getResults: get an array with the results
|
578 |
-
*/
|
579 |
-
class DoofinderResults{
|
580 |
-
|
581 |
-
// doofinder status
|
582 |
-
const SUCCESS = 'success'; // everything ok
|
583 |
-
const NOTFOUND = 'notfound'; // no account with the provided hashid found
|
584 |
-
const EXHAUSTED = 'exhausted'; // the account has reached its query limit
|
585 |
-
|
586 |
-
private $properties = null;
|
587 |
-
private $results = null;
|
588 |
-
private $facets = null;
|
589 |
-
private $filter = null;
|
590 |
-
public $status = null;
|
591 |
-
|
592 |
-
/**
|
593 |
-
* Constructor
|
594 |
-
*
|
595 |
-
* @param string $jsonString stringified json returned by doofinder search server
|
596 |
-
*/
|
597 |
-
function __construct($jsonString){
|
598 |
-
$rawResults = json_decode($jsonString, true);
|
599 |
-
foreach($rawResults as $kkey => $vall){
|
600 |
-
if(!is_array($vall)){
|
601 |
-
$this->properties[$kkey] = $vall;
|
602 |
-
}
|
603 |
-
}
|
604 |
-
// doofinder status
|
605 |
-
$this->status = isset($this->properties['doofinder_status'])?
|
606 |
-
$this->properties['doofinder_status'] : self::SUCCESS;
|
607 |
-
|
608 |
-
// results
|
609 |
-
$this->results = array();
|
610 |
-
|
611 |
-
if(isset($rawResults['results']) && is_array($rawResults['results']))
|
612 |
-
{
|
613 |
-
$this->results = $rawResults['results'];
|
614 |
-
}
|
615 |
-
|
616 |
-
// build a friendly filter array
|
617 |
-
$this->filter = array();
|
618 |
-
// reorder filter, before assigning it to $this
|
619 |
-
if(isset($rawResults['filter']))
|
620 |
-
{
|
621 |
-
foreach($rawResults['filter'] as $filterType => $filters)
|
622 |
-
{
|
623 |
-
foreach($filters as $filterName => $filterProperties)
|
624 |
-
{
|
625 |
-
$this->filter[$filterName] = $filterProperties;
|
626 |
-
}
|
627 |
-
}
|
628 |
-
}
|
629 |
-
|
630 |
-
// facets
|
631 |
-
$this->facets = array();
|
632 |
-
if(isset($rawResults['facets']))
|
633 |
-
{
|
634 |
-
$this->facets = $rawResults['facets'];
|
635 |
-
|
636 |
-
// mark "selected" true or false according to filters presence
|
637 |
-
foreach($this->facets as $facetName => $facetProperties){
|
638 |
-
switch($facetProperties['_type']){
|
639 |
-
case 'terms':
|
640 |
-
foreach($facetProperties['terms'] as $pos => $term){
|
641 |
-
if(isset($this->filter[$facetName]) && in_array($term['term'], $this->filter[$facetName])){
|
642 |
-
$this->facets[$facetName]['terms'][$pos]['selected'] = true;
|
643 |
-
} else {
|
644 |
-
$this->facets[$facetName]['terms'][$pos]['selected'] = false;
|
645 |
-
}
|
646 |
-
}
|
647 |
-
break;
|
648 |
-
case 'range':
|
649 |
-
foreach($facetProperties['ranges'] as $pos => $range){
|
650 |
-
$this->facets[$facetName]['ranges'][$pos]['selected_from'] = false;
|
651 |
-
$this->facets[$facetName]['ranges'][$pos]['selected_to'] = false;
|
652 |
-
if(isset($this->filter[$facetName]) && isset($this->filter[$facetName]['gte'])){
|
653 |
-
$this->facets[$facetName]['ranges'][$pos]['selected_from'] = $this->filter[$facetName]['gte'];
|
654 |
-
}
|
655 |
-
if(isset($this->filter[$facetName]) && isset($this->filter[$facetName]['lte'])){
|
656 |
-
$this->facets[$facetName]['ranges'][$pos]['selected_to'] = $this->filter[$facetName]['lte'];
|
657 |
-
}
|
658 |
-
|
659 |
-
}
|
660 |
-
break;
|
661 |
-
}
|
662 |
-
}
|
663 |
-
}
|
664 |
-
}
|
665 |
-
|
666 |
-
/**
|
667 |
-
* getProperty
|
668 |
-
*
|
669 |
-
* get single property from the results
|
670 |
-
* @param string @propertyName: 'results_per_page', 'query', 'max_score', 'page', 'total', 'hashid'
|
671 |
-
* @return mixed the value of the property
|
672 |
-
*/
|
673 |
-
public function getProperty($propertyName){
|
674 |
-
return array_key_exists($propertyName, $this->properties) ?
|
675 |
-
$this->properties[$propertyName]: null;
|
676 |
-
}
|
677 |
-
|
678 |
-
/**
|
679 |
-
* getResults
|
680 |
-
*
|
681 |
-
* @return array search results. at the moment, only the 'cooked' version.
|
682 |
-
* Each result is of the form:
|
683 |
-
* array('header'=>...,
|
684 |
-
* 'body' => ..,
|
685 |
-
* 'price' => ..,
|
686 |
-
* 'href' => ...,
|
687 |
-
* 'image' => ...,
|
688 |
-
* 'type' => ...,
|
689 |
-
* 'id' => ..)
|
690 |
-
*/
|
691 |
-
public function getResults(){
|
692 |
-
return $this->results;
|
693 |
-
}
|
694 |
-
|
695 |
-
/**
|
696 |
-
*
|
697 |
-
* getFacetsNames
|
698 |
-
*
|
699 |
-
* @return array facets names.
|
700 |
-
*/
|
701 |
-
public function getFacetsNames(){
|
702 |
-
return array_keys($this->facets);
|
703 |
-
}
|
704 |
-
|
705 |
-
/**
|
706 |
-
* getFacet
|
707 |
-
*
|
708 |
-
* @param string name the facet name whose results are wanted
|
709 |
-
*
|
710 |
-
* @return array facet search data
|
711 |
-
* - for terms facets
|
712 |
-
* array(
|
713 |
-
* '_type'=> 'terms', // type of facet 'terms' or 'range'
|
714 |
-
* 'missing'=> 3, // # of elements with no value for this facet
|
715 |
-
* 'others'=> 2, // # of terms not present in the search response
|
716 |
-
* 'total'=> 6, // # number of possible terms for this facet
|
717 |
-
* 'terms'=> array(
|
718 |
-
* array('count'=>6, 'term'=>'Blue', 'selected'=>false), // in the response, there are 6 'blue' terms
|
719 |
-
* array('count'=>3, 'term': 'Red', 'selected'=>true), // if 'selected'=>true, that term has been selected as filter
|
720 |
-
* ...
|
721 |
-
* )
|
722 |
-
* )
|
723 |
-
* - for range facets
|
724 |
-
* array(
|
725 |
-
* '_type'=> 'range',
|
726 |
-
* 'ranges'=> array(
|
727 |
-
* array(
|
728 |
-
* 'count'=>6, // in the response, 6 elements within that range.
|
729 |
-
* 'from':0,
|
730 |
-
* 'min': 30
|
731 |
-
* 'max': 90,
|
732 |
-
* 'mean'=>33.2,
|
733 |
-
* 'total'=>432,
|
734 |
-
* 'total_count'=>6,
|
735 |
-
* 'selected_from'=> 34.3 // if present. this value has been used as filter. false otherwise
|
736 |
-
* 'selected_to'=> 99.3 // if present. this value has been used as filter. false otherwise
|
737 |
-
* ),
|
738 |
-
* ...
|
739 |
-
* )
|
740 |
-
* )
|
741 |
-
*
|
742 |
-
*
|
743 |
-
*/
|
744 |
-
public function getFacet($facetName){
|
745 |
-
return $this->facets[$facetName];
|
746 |
-
}
|
747 |
-
|
748 |
-
/**
|
749 |
-
* getFacets
|
750 |
-
*
|
751 |
-
* get the whole facets associative array:
|
752 |
-
* array('color'=>array(...), 'brand'=>array(...))
|
753 |
-
* each array is defined as in getFacet() docstring
|
754 |
-
*
|
755 |
-
* @return array facets assoc. array
|
756 |
-
*/
|
757 |
-
public function getFacets(){
|
758 |
-
return $this->facets;
|
759 |
-
}
|
760 |
-
|
761 |
-
/**
|
762 |
-
* getAppliedFilters
|
763 |
-
*
|
764 |
-
* get the filters the query has defined
|
765 |
-
* array('categories' => array( // filter name . same as facet name
|
766 |
-
* 'Sillas de paseo', // if simple array, it's a terms facet
|
767 |
-
* 'Sacos sillas de paseo'
|
768 |
-
* ),
|
769 |
-
* 'color' => array(
|
770 |
-
* 'red',
|
771 |
-
* 'blue'
|
772 |
-
* ),
|
773 |
-
* 'price' => array(
|
774 |
-
* 'include_upper'=>true, // if 'from' , 'to' keys, it's a range facet
|
775 |
-
* 'from'=>35.19,
|
776 |
-
* 'to'=>9999
|
777 |
-
* )
|
778 |
-
* )
|
779 |
-
* MEANING OF THE EXAMPLE FILTER:
|
780 |
-
* "FROM the query results, filter only results that have ('Sillas de paseo' OR 'Sacos sillas de paseo') categories
|
781 |
-
* AND ('red' OR 'blue') color AND price is BETWEEN 34.3 and 99.3"
|
782 |
-
|
783 |
-
*/
|
784 |
-
public function getAppliedFilters(){
|
785 |
-
return $this->filter;
|
786 |
-
}
|
787 |
-
|
788 |
-
/**
|
789 |
-
* isOk
|
790 |
-
*
|
791 |
-
* checks if all went well
|
792 |
-
* @return boolean true if the status is 'success'.
|
793 |
-
* false if the status is not.
|
794 |
-
*/
|
795 |
-
public function isOk(){
|
796 |
-
return $this->status == self::SUCCESS;
|
797 |
-
}
|
798 |
-
}
|
799 |
-
|
800 |
-
|
801 |
-
class DoofinderException extends Exception{
|
802 |
-
|
803 |
-
}
|
804 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Doofinder/doofinder_management_api.php
DELETED
@@ -1,408 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Author:: JoeZ99 (<jzarate@gmail.com>).
|
4 |
-
*
|
5 |
-
* License:: Apache License, Version 2.0
|
6 |
-
*
|
7 |
-
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
8 |
-
* not use this file except in compliance with the License. You may obtain
|
9 |
-
* a copy of the License at
|
10 |
-
*
|
11 |
-
* http://www.apache.org/licenses/LICENSE-2.0
|
12 |
-
*
|
13 |
-
* Unless required by applicable law or agreed to in writing, software
|
14 |
-
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15 |
-
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16 |
-
* License for the specific language governing permissions and limitations
|
17 |
-
* under the License.
|
18 |
-
*/
|
19 |
-
require_once dirname(__FILE__).'/errors.php';
|
20 |
-
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Class to manage the connection with the API servers.
|
24 |
-
*
|
25 |
-
* Needs APIKEY in initialization.
|
26 |
-
* Example: $dma = new DoofinderManagementApi('eu1-d531af87f10969f90792a4296e2784b089b8a875')
|
27 |
-
*/
|
28 |
-
class DoofinderManagementApi
|
29 |
-
{
|
30 |
-
const REMOTE_API_ENDPOINT = "https://%s-api.doofinder.com/v1";
|
31 |
-
const LOCAL_API_ENDPOINT = "http://localhost:8000/api/v1";
|
32 |
-
|
33 |
-
private $apiKey = null;
|
34 |
-
private $clusterRegion = 'eu1';
|
35 |
-
private $token = null;
|
36 |
-
private $baseManagementUrl = null;
|
37 |
-
|
38 |
-
function __construct($apiKey, $local = false){
|
39 |
-
$this->apiKey = $apiKey;
|
40 |
-
$clusterToken = explode('-', $apiKey);
|
41 |
-
$this->clusterRegion = $clusterToken[0];
|
42 |
-
$this->token = $clusterToken[1];
|
43 |
-
if ($local === true){
|
44 |
-
$this->baseManagementUrl = self::LOCAL_API_ENDPOINT;
|
45 |
-
} else {
|
46 |
-
$this->baseManagementUrl = sprintf(self::REMOTE_API_ENDPOINT, $this->clusterRegion);
|
47 |
-
}
|
48 |
-
}
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Makes the actual request to the API server and normalize response
|
52 |
-
*
|
53 |
-
* @param string $method The HTTP method to use. 'GET|PUT|POST|DELETE'
|
54 |
-
* @param string $entryPoint The path to use. '/<hashid>/items/product'
|
55 |
-
* @param array $params If any, url request parameters
|
56 |
-
* @param array $data If any, body request parameters
|
57 |
-
* @return array Array with both status code and response .
|
58 |
-
*/
|
59 |
-
function managementApiCall($method='GET', $entryPoint='', $params=null, $data=null){
|
60 |
-
$headers = array('Authorization: Token '.$this->token, // for Auth
|
61 |
-
'Content-Type: application/json',
|
62 |
-
'Expect:'); // Fixes the HTTP/1.1 417 Expectation Failed
|
63 |
-
|
64 |
-
$url = $this->baseManagementUrl.'/'.$entryPoint;
|
65 |
-
if (is_array($params) && sizeof($params) > 0){
|
66 |
-
$url .= '?'.http_build_query($params);
|
67 |
-
}
|
68 |
-
|
69 |
-
$session = curl_init($url);
|
70 |
-
curl_setopt($session, CURLOPT_CUSTOMREQUEST, $method);
|
71 |
-
curl_setopt($session, CURLOPT_HEADER, false);
|
72 |
-
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Tell curl to return the response
|
73 |
-
curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Adding request headers
|
74 |
-
|
75 |
-
if (in_array($method, array('POST', 'PUT'))){
|
76 |
-
curl_setopt($session, CURLOPT_POSTFIELDS, $data);
|
77 |
-
}
|
78 |
-
|
79 |
-
$response = curl_exec($session);
|
80 |
-
$httpCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
81 |
-
curl_close($session);
|
82 |
-
|
83 |
-
handleErrors($httpCode, $response);
|
84 |
-
|
85 |
-
$return = array('statusCode' => $httpCode);
|
86 |
-
$return['response'] = ($decoded = json_decode($response, true)) ? $decoded : $response;
|
87 |
-
|
88 |
-
return $return;
|
89 |
-
}
|
90 |
-
|
91 |
-
/**
|
92 |
-
* To get info on all possible api entry points
|
93 |
-
* @return array An assoc. array with the different entry points
|
94 |
-
*/
|
95 |
-
function getApiRoot(){
|
96 |
-
$response = $this->managementApiCall();
|
97 |
-
return $response['response'];
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Obtain a list of SearchEngines objects, ready to interact with the API
|
102 |
-
* @return array list of searchEngines objects
|
103 |
-
*/
|
104 |
-
function getSearchEngines(){
|
105 |
-
$searchEngines = array();
|
106 |
-
$apiRoot = $this->getApiRoot();
|
107 |
-
unset($apiRoot['searchengines']);
|
108 |
-
|
109 |
-
foreach($apiRoot as $hashid => $props){
|
110 |
-
$searchEngines[] = new SearchEngine($this, $hashid, $props['name']);
|
111 |
-
}
|
112 |
-
|
113 |
-
return $searchEngines;
|
114 |
-
}
|
115 |
-
|
116 |
-
}
|
117 |
-
|
118 |
-
/**
|
119 |
-
* Class with all the capabilities described in the API
|
120 |
-
*
|
121 |
-
* see http://www.doofinder.com/developer/topics/api/management-api
|
122 |
-
*/
|
123 |
-
class SearchEngine {
|
124 |
-
public $name = null;
|
125 |
-
public $hashid = null;
|
126 |
-
|
127 |
-
private $dma = null; // DoofinderManagementApi instance
|
128 |
-
|
129 |
-
function __construct($dma, $hashid, $name){
|
130 |
-
$this->name = $name;
|
131 |
-
$this->hashid = $hashid;
|
132 |
-
$this->dma = $dma;
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Get a list of searchengine's types
|
137 |
-
*
|
138 |
-
* @return array list of types
|
139 |
-
*/
|
140 |
-
function getDatatypes(){
|
141 |
-
return $this->getTypes();
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Get a list of searchengine's types
|
146 |
-
*
|
147 |
-
* @return array list of types
|
148 |
-
*/
|
149 |
-
function getTypes(){
|
150 |
-
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/types");
|
151 |
-
return $result['response'];
|
152 |
-
}
|
153 |
-
|
154 |
-
/**
|
155 |
-
* Add a type to the searchengine
|
156 |
-
*
|
157 |
-
* @param string $dType the type name
|
158 |
-
* @return new list of searchengine's types
|
159 |
-
*/
|
160 |
-
function addType($dType){
|
161 |
-
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/types", null,
|
162 |
-
json_encode(array('name' => $dType)));
|
163 |
-
return $result['response'];
|
164 |
-
}
|
165 |
-
|
166 |
-
/**
|
167 |
-
* Delete a type and all its items. HANDLE WITH CARE
|
168 |
-
*
|
169 |
-
* @param string $dType the Type to delete. All items belonging
|
170 |
-
* to that type will be removed. mandatory
|
171 |
-
* @return boolean true on success
|
172 |
-
*/
|
173 |
-
function deleteType($dType){
|
174 |
-
$result = $this->dma->managementApiCall('DELETE', "{$this->hashid}/types/{$dType}");
|
175 |
-
return $result['statusCode'] == 204;
|
176 |
-
}
|
177 |
-
|
178 |
-
/**
|
179 |
-
* Get paginated indexed items belonging to a searchengine's type
|
180 |
-
*
|
181 |
-
* It only paginates forward. Can't go backwards
|
182 |
-
* @param string $dType Type of the items to list
|
183 |
-
* @param string $scrollId identifier of the pagination set
|
184 |
-
* @return array Assoc array with scroll_id ,paginated results and total results.
|
185 |
-
*/
|
186 |
-
public function getScrolledItemsPage($dType, $scrollId = null){
|
187 |
-
$params = $scrollId ? array("scroll_id" => $scrollId) : null;
|
188 |
-
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/items/{$dType}", $params);
|
189 |
-
return array(
|
190 |
-
'scroll_id' => $result['response']['scroll_id'],
|
191 |
-
'results' => $result['response']['results'],
|
192 |
-
'total' => $result['response']['count'],
|
193 |
-
);
|
194 |
-
}
|
195 |
-
|
196 |
-
function items($dType){
|
197 |
-
return new ItemsRS($this, $dType);
|
198 |
-
}
|
199 |
-
|
200 |
-
/**
|
201 |
-
* Get details of a specific item
|
202 |
-
*
|
203 |
-
* @param string $dType Type of the item.
|
204 |
-
* @param string $itemId the id of the item
|
205 |
-
* @return array Assoc array representing the item.
|
206 |
-
*/
|
207 |
-
function getItem($dType, $itemId){
|
208 |
-
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/items/{$dType}/{$itemId}");
|
209 |
-
return $result['response'];
|
210 |
-
}
|
211 |
-
|
212 |
-
/**
|
213 |
-
* Add an item to the search engine
|
214 |
-
*
|
215 |
-
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
216 |
-
* item with that id.
|
217 |
-
* - It the 'id' field is not present, create one.
|
218 |
-
*
|
219 |
-
* @param string $dType type of the. If not provided, first available type is used
|
220 |
-
* @param array $itemDescription Assoc array representation of the item
|
221 |
-
* @return string the id of the item just created
|
222 |
-
*/
|
223 |
-
function addItem($dType, $itemDescription){
|
224 |
-
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/items/{$dType}", null,
|
225 |
-
json_encode($itemDescription));
|
226 |
-
return $result['response']['id'];
|
227 |
-
}
|
228 |
-
|
229 |
-
/**
|
230 |
-
* Add items in bulk to the search engine
|
231 |
-
*
|
232 |
-
* For each item:
|
233 |
-
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
234 |
-
* item with that id.
|
235 |
-
* - It the 'id' field is not present, create one.
|
236 |
-
*
|
237 |
-
* @param string $dType type of the. If not provided, first available type is used
|
238 |
-
* @param array $itemsDescription List of Assoc array representation of the item
|
239 |
-
* @return array List of ids of the added items
|
240 |
-
*/
|
241 |
-
function addItems($dType, $itemsDescription){
|
242 |
-
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/items/{$dType}", null,
|
243 |
-
json_encode($itemsDescription));
|
244 |
-
|
245 |
-
function fetchId($item){
|
246 |
-
return $item['id'];
|
247 |
-
};
|
248 |
-
|
249 |
-
return array_map('fetchId', $result['response']);
|
250 |
-
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* Update or create an item of the search engine
|
254 |
-
*
|
255 |
-
* In case of conflict between itemDescription's id or $itemId,
|
256 |
-
* the latter is used.
|
257 |
-
*
|
258 |
-
* @param string $dType type of the Item.
|
259 |
-
* @param string $itemId Id of the item to be updated/added
|
260 |
-
* @param array $itemDescription Assoc array representating the item.
|
261 |
-
* @return boolean true on success.
|
262 |
-
*/
|
263 |
-
function updateItem($dType, $itemId, $itemDescription){
|
264 |
-
$result = $this->dma->managementApiCall('PUT', "{$this->hashid}/items/{$dType}/{$itemId}", null,
|
265 |
-
json_encode($itemDescription));
|
266 |
-
return $result['statusCode'] == 200;
|
267 |
-
}
|
268 |
-
|
269 |
-
/**
|
270 |
-
* Bulk update of several items
|
271 |
-
*
|
272 |
-
* Each item description must contain the 'id' field
|
273 |
-
*
|
274 |
-
* @param string $dType type of the items.
|
275 |
-
* @param array $itemsDescription List of assoc array representing items
|
276 |
-
* @return boolean true on success
|
277 |
-
*/
|
278 |
-
function updateItems($dType, $itemsDescription){
|
279 |
-
$result = $this->dma->managementApiCall('PUT', "{$this->hashid}/items/{$dType}", null,
|
280 |
-
json_encode($itemsDescription));
|
281 |
-
return $result['statusCode'] == 200;
|
282 |
-
}
|
283 |
-
|
284 |
-
/**
|
285 |
-
* Delete an item
|
286 |
-
*
|
287 |
-
* @param string $dType type of the item
|
288 |
-
* @param string $itemId id of the item
|
289 |
-
* @return boolean true if success, false if failure
|
290 |
-
*/
|
291 |
-
function deleteItem($dType, $itemId){
|
292 |
-
$result = $this->dma->managementApiCall('DELETE', "{$this->hashid}/items/{$dType}/{$itemId}");
|
293 |
-
return $result['statusCode'] == 204 ;
|
294 |
-
}
|
295 |
-
|
296 |
-
/**
|
297 |
-
* Ask the server to process the search engine's feeds
|
298 |
-
*
|
299 |
-
* @return array Assoc array with:
|
300 |
-
* - 'task_created': boolean true if a new task has been created
|
301 |
-
* - 'task_id': if task created, the id of the task.
|
302 |
-
*/
|
303 |
-
function process(){
|
304 |
-
$result = $this->dma->managementApiCall('POST', "{$this->hashid}/tasks/process");
|
305 |
-
$taskCreated = ($result['statusCode'] == 201);
|
306 |
-
$taskId = $taskCreated ? obtainId($result['response']['link']) : null;
|
307 |
-
return array('task_created'=>$taskCreated, 'task_id' => $taskId);
|
308 |
-
}
|
309 |
-
|
310 |
-
/**
|
311 |
-
* Obtain info of the last processing task sent to the server
|
312 |
-
*
|
313 |
-
* @return array Assoc array with 'state' and 'message' indicating status of the
|
314 |
-
* last asked processing task
|
315 |
-
*/
|
316 |
-
function processInfo(){
|
317 |
-
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/tasks/process");
|
318 |
-
unset($result['response']['task_name']);
|
319 |
-
return $result['response'];
|
320 |
-
}
|
321 |
-
|
322 |
-
/**
|
323 |
-
* Obtain info about how a task is going or its result
|
324 |
-
*
|
325 |
-
* @return array Assoc array with 'state' and 'message' indicating the status
|
326 |
-
* of the task
|
327 |
-
*/
|
328 |
-
function taskInfo($taskId){
|
329 |
-
$result = $this->dma->managementApiCall('GET', "{$this->hashid}/tasks/{$taskId}");
|
330 |
-
unset($result['response']['task_name']);
|
331 |
-
return $result['response'];
|
332 |
-
}
|
333 |
-
|
334 |
-
/**
|
335 |
-
* Obtain logs of the latest feed processing tasks done
|
336 |
-
*
|
337 |
-
* @return array list of arrays representing the logs
|
338 |
-
*/
|
339 |
-
function logs(){
|
340 |
-
$result = $this->dma->managementApiCall("GET", "{$this->hashid}/logs");
|
341 |
-
return $result['response'];
|
342 |
-
}
|
343 |
-
}
|
344 |
-
|
345 |
-
/**
|
346 |
-
* Helper class to iterate through the search engine's items
|
347 |
-
*
|
348 |
-
* Implemets Iterator interface so foreach() can work with ItemRS
|
349 |
-
*/
|
350 |
-
class ItemsRS implements Iterator {
|
351 |
-
|
352 |
-
private $searchEngine = null;
|
353 |
-
private $resultsPage = null;
|
354 |
-
private $scrollId = null;
|
355 |
-
private $position = 0;
|
356 |
-
private $total = null;
|
357 |
-
|
358 |
-
function __construct($searchEngine, $dType){
|
359 |
-
$this->dType = $dType;
|
360 |
-
$this->searchEngine = $searchEngine;
|
361 |
-
}
|
362 |
-
|
363 |
-
private function fetchResults(){
|
364 |
-
$apiResults = $this->searchEngine->getScrolledItemsPage($this->dType, $this->scrollId);
|
365 |
-
$this->total = $apiResults['total'];
|
366 |
-
$this->resultsPage = $apiResults['results'];
|
367 |
-
$this->scrollId = $apiResults['scroll_id'];
|
368 |
-
$this->currentItem = each($this->resultsPage);
|
369 |
-
}
|
370 |
-
|
371 |
-
function rewind(){
|
372 |
-
$this->scrollId = null;
|
373 |
-
$this->fetchResults();
|
374 |
-
}
|
375 |
-
|
376 |
-
function valid(){
|
377 |
-
return $this->position < $this->total;
|
378 |
-
}
|
379 |
-
|
380 |
-
function current(){
|
381 |
-
return $this->currentItem['value'];
|
382 |
-
}
|
383 |
-
|
384 |
-
function key(){
|
385 |
-
return $this->position;
|
386 |
-
}
|
387 |
-
|
388 |
-
function next(){
|
389 |
-
++$this->position;
|
390 |
-
$this->currentItem = each($this->resultsPage);
|
391 |
-
if (!$this->currentItem && $this->position < $this->total){
|
392 |
-
$this->fetchResults();
|
393 |
-
reset($this->resultsPage);
|
394 |
-
$this->currentItem = each($this->resultsPage);
|
395 |
-
}
|
396 |
-
}
|
397 |
-
}
|
398 |
-
|
399 |
-
/**
|
400 |
-
* Extracts identificator from an item or task url.
|
401 |
-
*
|
402 |
-
* @param string $url item or task resource locator
|
403 |
-
* @return the item identificator
|
404 |
-
*/
|
405 |
-
function obtainId($url){
|
406 |
-
preg_match('~/\w{32}/(items/\w+|tasks)/([\w-_]+)/?$~', $url, $matches);
|
407 |
-
return $matches[2];
|
408 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Doofinder/errors.php
DELETED
@@ -1,48 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class NotAllowed extends Exception {}
|
4 |
-
|
5 |
-
class BadRequest extends Exception {}
|
6 |
-
|
7 |
-
class QuotaExhausted extends Exception {}
|
8 |
-
|
9 |
-
class WrongResponse extends Exception {}
|
10 |
-
|
11 |
-
class ThrottledResponse extends Exception {}
|
12 |
-
|
13 |
-
function readError($response) {
|
14 |
-
$error = json_decode($response, true);
|
15 |
-
$error = $error['detail'];
|
16 |
-
if (!isset($error)) {
|
17 |
-
$error = $response;
|
18 |
-
}
|
19 |
-
return $error;
|
20 |
-
}
|
21 |
-
|
22 |
-
function handleErrors($statusCode, $response){
|
23 |
-
switch($statusCode)
|
24 |
-
{
|
25 |
-
case 403:
|
26 |
-
throw new NotAllowed("The user does not have permissions to perform this operation: ".readError($response));
|
27 |
-
case 401:
|
28 |
-
throw new NotAllowed("The user hasn't provided valid authorization: ".readError($response));
|
29 |
-
case 404:
|
30 |
-
throw new BadRequest("Not Found: ".readError($response));
|
31 |
-
case 409: // trying to post with an already used id
|
32 |
-
throw new BadRequest("Request conflict: ".readError($response));
|
33 |
-
case 429:
|
34 |
-
if (stripos($response, 'throttled')) {
|
35 |
-
throw new ThrottledResponse(readError($response));
|
36 |
-
} else {
|
37 |
-
throw new QuotaExhausted("The query quota has been reached. No more queries can be requested right now");
|
38 |
-
}
|
39 |
-
}
|
40 |
-
|
41 |
-
if ($statusCode >= 500) {
|
42 |
-
throw new WrongResponse("Server error: ".readError($response));
|
43 |
-
}
|
44 |
-
|
45 |
-
if ($statusCode >= 400) {
|
46 |
-
throw new BadRequest("The client made a bad request: ".readError($response));
|
47 |
-
}
|
48 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/php-doofinder/CHANGELOG.txt
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Version 4.
|
2 |
+
----------
|
3 |
+
|
4 |
+
- CamelCase convention for everything. "has_next(0" is now "hasNext()"
|
5 |
+
- And empty query() prompts a "match all" query
|
6 |
+
- Facets/Filtering support
|
7 |
+
|
8 |
+
Version 5.
|
9 |
+
----------
|
10 |
+
|
11 |
+
- API Key Authorization. API Key required in client constructor
|
12 |
+
- Allow unprefixed params
|
13 |
+
- Allow custom query parameter name
|
14 |
+
- v5.2.1
|
15 |
+
- Search Client: getOptions method and https mandatory
|
16 |
+
- v5.2.2
|
17 |
+
- mainteinance release
|
18 |
+
- v5.2.3
|
19 |
+
- bugfixes
|
20 |
+
- v5.2.4
|
21 |
+
- bugfixes
|
22 |
+
- v5.2.5
|
23 |
+
- stats retrieval
|
24 |
+
- v5.2.6
|
25 |
+
- added sort parameter.
|
26 |
+
- v5.3.1
|
27 |
+
- complete rewrite of the file structure: psr4 compliant
|
28 |
+
- added to the composer repository
|
29 |
+
- v5.4.3
|
30 |
+
- bugfixes
|
31 |
+
- autoload for those not using composes
|
32 |
+
- decent formatting
|
33 |
+
- v5.5.0
|
34 |
+
- Added phpunit tests
|
35 |
+
- Added phpunit tests
|
36 |
+
- some small refactoring
|
37 |
+
- v5.5.1
|
38 |
+
- added deleteItems method
|
lib/php-doofinder/README.md
ADDED
@@ -0,0 +1,599 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[![Build Status](https://api.travis-ci.org/doofinder/php-doofinder.svg?branch=master)](https://travis-ci.org/doofinder/php-doofinder)
|
2 |
+
# Official PHP Client for doofinder
|
3 |
+
|
4 |
+
<!-- MarkdownTOC depth=3 -->
|
5 |
+
|
6 |
+
- [Installation](#installation)
|
7 |
+
- [Download Method](#download-method)
|
8 |
+
- [Using Composer](#using-composer)
|
9 |
+
- [Search Client](#search-client)
|
10 |
+
- [Quick & Dirty](#quick--dirty)
|
11 |
+
- [Searching from HTML Forms](#searching-from-html-forms)
|
12 |
+
- [Tips](#tips)
|
13 |
+
- [Extra Search Options](#extra-search-options)
|
14 |
+
- [Extra Constructor Options](#extra-constructor-options)
|
15 |
+
- [API reference](#api-reference)
|
16 |
+
- [One quick example](#one-quick-example)
|
17 |
+
- [Management API](#management-api)
|
18 |
+
- [Quick & Dirty](#quick--dirty-1)
|
19 |
+
|
20 |
+
<!-- /MarkdownTOC -->
|
21 |
+
|
22 |
+
## Installation
|
23 |
+
|
24 |
+
To install the library you can download it from the project's [releases](https://github.com/doofinder/php-doofinder/releases) page or use [Composer](https://packagist.org/packages/doofinder/doofinder).
|
25 |
+
|
26 |
+
Requires PHP 5.3 or later. Not tested in previous versions.
|
27 |
+
|
28 |
+
### Download Method
|
29 |
+
|
30 |
+
Just include the provided `autoload.php` file and use:
|
31 |
+
|
32 |
+
```php
|
33 |
+
require_once('path/to/php-doofinder/autoload.php');
|
34 |
+
$client = new \Doofinder\Api\Search\Client(HASHID, API_KEY);
|
35 |
+
```
|
36 |
+
|
37 |
+
### Using Composer
|
38 |
+
|
39 |
+
Add Doofinder to your `composer.json` file by running:
|
40 |
+
|
41 |
+
```bash
|
42 |
+
$ composer require doofinder/doofinder
|
43 |
+
```
|
44 |
+
|
45 |
+
If you're already using composer your autoload.php file will be updated. If not, a new one will be generated and you will have to include it:
|
46 |
+
|
47 |
+
```php
|
48 |
+
<?php
|
49 |
+
require_once dirname(__FILE__).'/vendor/autoload.php';
|
50 |
+
|
51 |
+
use \Doofinder\Api\Search\Client as SearchClient;
|
52 |
+
use \Doofinder\Api\Management\Client as ManagementClient;
|
53 |
+
|
54 |
+
$client = new SearchClient(HASHID, API_KEY);
|
55 |
+
```
|
56 |
+
|
57 |
+
## Search Client
|
58 |
+
|
59 |
+
### Quick & Dirty
|
60 |
+
|
61 |
+
```php
|
62 |
+
require_once('path/to/php-doofinder/autoload.php');
|
63 |
+
|
64 |
+
define('HASHID', '6a9gbc4dcx735x123b1e0198gf92e6e9');
|
65 |
+
define('API_KEY', 'eu1-384fdag73c7ff0a59g589xf9f4083bxb9727f9c3')
|
66 |
+
|
67 |
+
// Set hashid and API Key
|
68 |
+
$client = new \Doofinder\Api\Search\Client(HASHID, API_KEY);
|
69 |
+
|
70 |
+
// You can specify filters
|
71 |
+
$client->setFilter('brand', array('nike', 'converse')); // brand must be 'nike' or 'converse' AND ...
|
72 |
+
$client->setFilter('color', array('red', 'blue')); // ... color must be 'red' or 'blue' AND ...
|
73 |
+
$client->setFilter('price', array('from'=>33.2)); // ... price must be upper than 33.2
|
74 |
+
|
75 |
+
// You can also use more specific methods for that.
|
76 |
+
$client->addTerm('brand', 'adidas'); // add 'adidas' to the 'brand' filter
|
77 |
+
$client->removeTerm('brand', 'nike'); // remove 'nike' from the 'brand' filter
|
78 |
+
$client->setRange('price', null, 99.9); // add an upper limit to the price
|
79 |
+
|
80 |
+
// Feeling adventurous? sort!
|
81 |
+
|
82 |
+
$client->addSort('price', 'desc'); // sort by price (descending)...
|
83 |
+
$client->addSort('title', 'asc'); // ... and then by title (ascending)
|
84 |
+
|
85 |
+
// Do the query, specify the page if you want.
|
86 |
+
// 'page' = 1. optional . 'transformer' = 'dflayer'. optional.
|
87 |
+
$results = $client->query('test query', 1, array('transformer' => 'dflayer'));
|
88 |
+
|
89 |
+
// With the results object, fetch specific properties, facets or the results
|
90 |
+
// itself as an array.
|
91 |
+
|
92 |
+
$results->getProperty('results_per_page'); // returns results per page.
|
93 |
+
$results->getProperty('page'); // returns the page of the results
|
94 |
+
$results->getProperty('total'); // total number of results
|
95 |
+
$results->getProperty('query'); // query used
|
96 |
+
$results->getProperty('hashid');
|
97 |
+
$results->getProperty('max_score'); // maximum score obtained in the search results
|
98 |
+
$results->getProperty('doofinder_status'); // special Doofinder status, see below
|
99 |
+
|
100 |
+
// If you use the 'dflayer' transformer ...
|
101 |
+
foreach($results->getResults() as $result){
|
102 |
+
echo $result['body']."\n"; // description of the item
|
103 |
+
echo $result['dfid']."\n"; // doofinder id. uniquely identifies this item
|
104 |
+
echo $result['price']."\n"; // string, may come with currency sign
|
105 |
+
echo $result['sale_price']."\n"; // may or may not be present
|
106 |
+
echo $result['header']."\n"; // title of the item
|
107 |
+
echo $result['href']."\n" ; // url of the item's page
|
108 |
+
echo $result['image']."\n" ; // url of the item's image
|
109 |
+
echo $result['type']."\n" ; // item's type. "product" at the moment
|
110 |
+
echo $result['id']."\n" ; // item's id, as it comes from the xml feed
|
111 |
+
}
|
112 |
+
|
113 |
+
$category_facet = $results->getFacet('category');
|
114 |
+
|
115 |
+
foreach($category_facet['terms'] as $term) {
|
116 |
+
// Category: Trousers : 5 results found
|
117 |
+
echo "Category: ".$term['term']." : ".$term['count']." results found\n";
|
118 |
+
}
|
119 |
+
|
120 |
+
$price_facet = $results->getFacet('price');
|
121 |
+
|
122 |
+
echo "Min price found: ".$price_facet['ranges'][0]['min']."\n";
|
123 |
+
// Min price found: 33.6
|
124 |
+
echo "Max price found: ".$price_facet['ranges'][0]['max']."\n";
|
125 |
+
```
|
126 |
+
|
127 |
+
__Notice:__
|
128 |
+
|
129 |
+
- For non-numeric fields you'll have to set those fields as _sortable_ in Doofinder's control panel before you can sort by them.
|
130 |
+
- Every search request is made through secure protocol.
|
131 |
+
|
132 |
+
### Searching from HTML Forms
|
133 |
+
|
134 |
+
#### Be careful with the `query_name` parameter
|
135 |
+
|
136 |
+
When you issue a query to Doofinder, the search engine tries different types of search in order to provide the best possible results. This "types of search" are controlled by the `query_name` parameter.
|
137 |
+
|
138 |
+
However, if you're going to apply filters to a query, that means you're going to make the search again with certain added restrictions, therefore you're not interested in let Doofinder find the best _"_type of search_"_ for you again, but you rather do the search exactly the same way you did when first querying, so the results with applied filters are consistent with that.
|
139 |
+
|
140 |
+
`$results->getProperty('query_name')` gives you the type of query that was used to fetch those results. If you plan to filter on those, you should use the same type of query. You can do that with:
|
141 |
+
|
142 |
+
```php
|
143 |
+
// make the initial query. no filters and no "query_name" specified
|
144 |
+
$results = $client->query("baby gloves");
|
145 |
+
// "query_name" is automatically set to be the same it used for the first query
|
146 |
+
// add a filter
|
147 |
+
$client->addTerm('category', 'More than 6 years');
|
148 |
+
// do the same query. this time filtered and with a specific query_name
|
149 |
+
$client->query("baby gloves");
|
150 |
+
```
|
151 |
+
|
152 |
+
or with a form parameter:
|
153 |
+
|
154 |
+
```html
|
155 |
+
<form>
|
156 |
+
<input type="text" name="dfParam_query">
|
157 |
+
<input type="hidden" name="dfParam_query_name" value="text_all">
|
158 |
+
...
|
159 |
+
</form>
|
160 |
+
```
|
161 |
+
|
162 |
+
In short:
|
163 |
+
|
164 |
+
- You make a query to Doofinder either with filters or not. You don't need to specify `query_name`. Doofinder will find the more suitable `query_name`.
|
165 |
+
- Once you got the results, you can use `$results->getProperty('query_name')` to know which `query_name` was the one Doofinder chose.
|
166 |
+
- If you want to make further filtering on those search results, you should instruct Doofinder to use the same `query_name` you got from the first search results.
|
167 |
+
- Each time you do any new query, don't specify `query_name`. Let Doofinder find the best.
|
168 |
+
- **Warning:** don't try to figure out a `query_name` on your own. query names may change in the future. You can always count on `$results->getParameter('query_name')` to get the `query_name` that led to those `$results`
|
169 |
+
|
170 |
+
#### `toQuerystring()`
|
171 |
+
|
172 |
+
Dumps the complete state of the client (query, page, filters, rpp) into a querystring. Every param has the (configurable) `dfParam_` prefix to avoid conflicts.
|
173 |
+
|
174 |
+
```php
|
175 |
+
$page = 3; // Results page number. Optional.
|
176 |
+
echo $client->toQuerystring($page);
|
177 |
+
// query=dfParam_test+query&dfParam_rpp=4&dfParam_timeout=8000&dfParam_page=3
|
178 |
+
```
|
179 |
+
|
180 |
+
You can use it to build links to search results:
|
181 |
+
|
182 |
+
```html
|
183 |
+
<a href="results.php?<?php echo $client->toQuerystring(4)?>">Next Page</a>
|
184 |
+
```
|
185 |
+
|
186 |
+
#### `fromQuerystring()`
|
187 |
+
|
188 |
+
Gets information from the PHP request globals and initialises the client with search parameters (query, page, filters, rpp).
|
189 |
+
|
190 |
+
```php
|
191 |
+
$client = new \Doofinder\Api\Search\Client(HASHID, API_KEY);
|
192 |
+
$client->fromQuerystring();
|
193 |
+
$results = $client->query();
|
194 |
+
```
|
195 |
+
|
196 |
+
You can do the same in the constructor by passing `true` as the third parameter. This code is equivalent to the code above:
|
197 |
+
|
198 |
+
```php
|
199 |
+
<?php
|
200 |
+
$client = new \Doofinder\Api\Search\Client(HASHID, API_KEY, true);
|
201 |
+
$results = $client->query();
|
202 |
+
```
|
203 |
+
|
204 |
+
#### Filter Parameters
|
205 |
+
|
206 |
+
When specifying filters in HTML forms, follow this convention:
|
207 |
+
|
208 |
+
- All the filters are passed in an array called _filter_ prefixed with the _prefix_ specified in `$client` constructor (default: `dfParam_`).
|
209 |
+
- Each key is a filter name. Each value is filter definition.
|
210 |
+
- Filter definition for terms filter: use an array with terms
|
211 |
+
- Filter definition for range filter: use an array with `from` and/or `to` keys.
|
212 |
+
|
213 |
+
__Example:__ _color_ (terms filter) must be `blue` or `red` and _price_ (range filter) must be greater than `10.2`.
|
214 |
+
|
215 |
+
```html
|
216 |
+
<input name="dfParam_filter[color][]" value="blue">
|
217 |
+
<input name="dfParam_filter[color][]" value="red">
|
218 |
+
<input name="dfParam_filter[price][from]" value="10.2">
|
219 |
+
```
|
220 |
+
|
221 |
+
|
222 |
+
This constructs the array:
|
223 |
+
|
224 |
+
```php
|
225 |
+
dfParam_filter = array(
|
226 |
+
'color' => array('blue', 'red'),
|
227 |
+
'price' => array('from' => 10.2)
|
228 |
+
);
|
229 |
+
```
|
230 |
+
|
231 |
+
#### Sort Parameters
|
232 |
+
|
233 |
+
As with other params, the parameters must be prefixed with the `prefix` specified in `$client` constructor (default: `dfParam_`).
|
234 |
+
|
235 |
+
If you sorting for one field in ascending order, you can simply send the `sort` parameter with the name of the field to sort by as value:
|
236 |
+
|
237 |
+
```html
|
238 |
+
<input name="dfParam_sort" value="price">
|
239 |
+
```
|
240 |
+
|
241 |
+
If you want to specify the sort direction, you'll have to to send, for the `sort` param, an array, being the key the field to sort on and the value either `asc` or `desc`:
|
242 |
+
|
243 |
+
```html
|
244 |
+
<input name="dfParam_sort[price]" value="desc".
|
245 |
+
```
|
246 |
+
|
247 |
+
If you want to sort by several fields, just compound the previous definition in an array.
|
248 |
+
|
249 |
+
__Example:__ sort in descending order by price and if same price, sort by title in ascending order.
|
250 |
+
|
251 |
+
```html
|
252 |
+
<input name="dfParam_sort[0][price]" value="desc">
|
253 |
+
<input name="dfParam_sort[1][title]" value="asc">
|
254 |
+
```
|
255 |
+
|
256 |
+
This constructs the array:
|
257 |
+
|
258 |
+
```php
|
259 |
+
dfParam_sort = array(
|
260 |
+
array('price' => 'desc'),
|
261 |
+
array('title', 'asc')
|
262 |
+
);
|
263 |
+
```
|
264 |
+
|
265 |
+
Please read carefully the [sort parameters](http://www.doofinder.com/support/developer/api/search-api#sort-parameters) section in our search API documentation.
|
266 |
+
|
267 |
+
### Tips
|
268 |
+
|
269 |
+
#### Empty queries
|
270 |
+
|
271 |
+
An empty query matches all documents. Of course, if the query is filtered, even if the search term is none, the results are filtered too.
|
272 |
+
|
273 |
+
#### UTF-8 encoding
|
274 |
+
|
275 |
+
The results are always in UTF-8 encoding. If you're using it on an ISO-8859-1 encoded page, you can use `utf8_decode`:
|
276 |
+
|
277 |
+
```php
|
278 |
+
foreach ($results->getResults() as $result) {
|
279 |
+
echo utf8_decode($result['body']).PHP_EOL;
|
280 |
+
}
|
281 |
+
```
|
282 |
+
|
283 |
+
### Extra Search Options
|
284 |
+
|
285 |
+
```php
|
286 |
+
$query = 'test query';
|
287 |
+
$page = 3;
|
288 |
+
$results = $client->query($query, $page, array(
|
289 |
+
// Results Per Page (default: 10)
|
290 |
+
'rpp' => 4,
|
291 |
+
// types of item to search (default: all)
|
292 |
+
'types' => array('product', 'question'),
|
293 |
+
// Template used to return items
|
294 |
+
'transformer' => 'dflayer',
|
295 |
+
// Filtering options
|
296 |
+
'filter' => array(
|
297 |
+
'brand' => array('nike', 'converse'),
|
298 |
+
'price' => array('from'=> 33.2, 'to'=> 99),
|
299 |
+
),
|
300 |
+
// Sorting options
|
301 |
+
'sort' => array(
|
302 |
+
array('price' => 'asc'),
|
303 |
+
array('title' => 'desc'),
|
304 |
+
)
|
305 |
+
));
|
306 |
+
```
|
307 |
+
|
308 |
+
### Extra Constructor Options
|
309 |
+
|
310 |
+
```php
|
311 |
+
$client = new \Doofinder\Api\Search\Client(
|
312 |
+
HASHID,
|
313 |
+
API_KEY,
|
314 |
+
true, // Whether to import request parameters or not (default: false)
|
315 |
+
array(
|
316 |
+
// Prefix to use with toQuerystring() (default: dfParam_)
|
317 |
+
'prefix' => 'sp_df_df_',
|
318 |
+
// Parameter name to use for the query parameter (default: query)
|
319 |
+
'queryParameter' => 'q',
|
320 |
+
// API version of the search server (default: 5)
|
321 |
+
'apiVersion' => '5',
|
322 |
+
// Use only parameters from $_POST or $_GET methods
|
323 |
+
// (default: unset, uses $_REQUEST)
|
324 |
+
'restrictedRequest' => 'post'
|
325 |
+
)
|
326 |
+
);
|
327 |
+
```
|
328 |
+
|
329 |
+
### API reference
|
330 |
+
|
331 |
+
#### `\Doofinder\Api\Search\Client`
|
332 |
+
|
333 |
+
```php
|
334 |
+
$client->query($query, $page, $options); // Perform search
|
335 |
+
$client->hasNext(); // Boolean, true if there is a next page of results
|
336 |
+
$client->hasPrev(); // Boolean, true if there is a prev page of results
|
337 |
+
$client->numPages(); // Total number of pages
|
338 |
+
$client->getPage(); // Get the actual page number
|
339 |
+
$client->setFilter($filterName, $filter); // Set a filter
|
340 |
+
$client->getFilter($filterName); // Get a filter by name
|
341 |
+
$client->getFilters(); // Get all filters
|
342 |
+
$client->addTerm($filterName, $term); // Add a term to a terms type $filterName
|
343 |
+
$client->removeTerm($filterName, $term);
|
344 |
+
$client->setRange($filterName, $from, $to); // Specify parameters for a range filter
|
345 |
+
$client->getFilter($filter_name); // Get filter specifications for $filter_name, if any
|
346 |
+
$client->getFilters(); // Get filter specifications for all defined filters
|
347 |
+
$client->addSort($sortField, $direction); // Tells doofinder to sort results
|
348 |
+
$client->setPrefix($prefix); // Sets prefix for dumping/recovering from querystring
|
349 |
+
$client->toQuerystring($page); // Dumps state info to a querystring
|
350 |
+
$client->fromQuerystring(); // Recover state from a querystring
|
351 |
+
$client->nextPage(); // Obtain results for the nextpage
|
352 |
+
$client->prevPage(); // Obtain results for the prev page
|
353 |
+
$client->numPages(); // Num of pages
|
354 |
+
$client->getRpp(); // Get the number of results per page
|
355 |
+
$client->getTimeout();
|
356 |
+
$client->setApiVersion($apiVersion); // Sets API version to use (default: 5)
|
357 |
+
```
|
358 |
+
|
359 |
+
#### `\Doofinder\Api\Search\Results`
|
360 |
+
|
361 |
+
```php
|
362 |
+
$results->getProperty($propertyName); // Get the property $propertyName
|
363 |
+
$results->getResults(); // Get results
|
364 |
+
$results->getFacetsNames(); // Array with facet names
|
365 |
+
$results->getFacet($facetName); // Obtain search results for facet $facetName
|
366 |
+
$results->getFacets(); // All facets
|
367 |
+
$results->getAppliedFilters(); // Filters that have been applied to obtain these results
|
368 |
+
$results->isOk(); // Checks if all went OK
|
369 |
+
$results->status; // Account status info. 'success', 'exhausted', 'notfound'
|
370 |
+
```
|
371 |
+
|
372 |
+
### One quick example
|
373 |
+
|
374 |
+
```php
|
375 |
+
<?php
|
376 |
+
require_once('path/to/php-doofinder/autoload.php');
|
377 |
+
|
378 |
+
define('HASHID', '6a9gbc4dcx735x123b1e0198gf92e6e9');
|
379 |
+
define('API_KEY', 'eu1-384fdag73c7ff0a59g589xf9f4083bxb9727f9c3')
|
380 |
+
|
381 |
+
$client = new \Doofinder\Api\Search\Client(HASHID, API_KEY, true);
|
382 |
+
// if no dfParam_query, fetch all the results, to fetch all possible facets
|
383 |
+
$results = $client->query(null, null, array('transformer'=>'dflayer'));
|
384 |
+
?>
|
385 |
+
|
386 |
+
<form method="get" action="">
|
387 |
+
<input type="text" name="dfParam_query" onchange="emptyQueryName()" value="<?php echo $results->getProperty('query') ?>">
|
388 |
+
<input type="hidden" name="dfParam_rpp" value="3">
|
389 |
+
<input type="hidden" name="dfParam_transformer" value="dflayer">
|
390 |
+
<!-- this has to be removed via javascript if we want doofinder to find the best search for us. -->
|
391 |
+
<input type="hidden" id="query_name" name="dfParam_query_name" value="<?php echo $results->getProperty('query_name') ?>">
|
392 |
+
<input type="submit" value="search!">
|
393 |
+
|
394 |
+
<p>Filter by:</p>
|
395 |
+
<ul>
|
396 |
+
<?php foreach ($results->getFacets() as $facetName => $facetResults): ?>
|
397 |
+
<li>
|
398 |
+
<?php echo $facetName ?>
|
399 |
+
<ul>
|
400 |
+
<?php if ($facetResults['_type'] == 'terms'): ?>
|
401 |
+
<?php foreach ($facetResults['terms'] as $term):?>
|
402 |
+
<li>
|
403 |
+
<input type="checkbox"
|
404 |
+
name="dfParam_filter[<?php echo $facetName ?>][]"
|
405 |
+
<?php echo $term['selected'] ? 'checked': ''?>
|
406 |
+
value="<?php echo $term['term']?>">
|
407 |
+
<?php echo $term['term']?>: <?php echo $term['count']?>
|
408 |
+
</li>
|
409 |
+
<?php endforeach ?>
|
410 |
+
<?php endif ?>
|
411 |
+
<?php if $facetResults['_type'] == 'range'): $range = $facetResults['ranges'][0]; ?>
|
412 |
+
<li>
|
413 |
+
Range: <?php echo $range['min']?> -- <?php echo $range['max']?><br/>
|
414 |
+
From: <input type="text" name="dfParam_filter[<?php echo $facetName?>][from]" value="<?php echo $range['selected_from']?>">
|
415 |
+
To: <input type="text" name="dfParam_filter[<?php echo $facetName?>][to]" value="<?php echo $range['selected_to']?>">
|
416 |
+
</li>
|
417 |
+
<?php endif?>
|
418 |
+
</ul>
|
419 |
+
</li>
|
420 |
+
<?php endforeach ?>
|
421 |
+
</ul>
|
422 |
+
</form>
|
423 |
+
|
424 |
+
<h1>Results</h1>
|
425 |
+
|
426 |
+
<ul>
|
427 |
+
<?php foreach ($results->getResults() as $result): ?>
|
428 |
+
<li><?php echo $result['header'] ?></li>
|
429 |
+
<?php endforeach ?>
|
430 |
+
</ul>
|
431 |
+
|
432 |
+
<?php if ($client->hasPrev()): ?>
|
433 |
+
<a href="?<?php echo $client->toQuerystring($client->getPage() - 1) ?>">Prev</a>
|
434 |
+
<?php endif?>
|
435 |
+
Number of pages: <?php echo $client->numPages() ?>
|
436 |
+
<?php if ($client->hasNext()): ?>
|
437 |
+
<a href="?<?php echo $client->toQuerystring($client->getPage() + 1) ?>">Next</a>
|
438 |
+
<?php endif?>
|
439 |
+
|
440 |
+
<script>
|
441 |
+
// if the search box changes, a new query is being made
|
442 |
+
// don't tell doofinder which search type to use
|
443 |
+
function emptyQueryName(){
|
444 |
+
document.getElementById('query_name').value = '';
|
445 |
+
return true;
|
446 |
+
}
|
447 |
+
</script>
|
448 |
+
```
|
449 |
+
|
450 |
+
## Management API
|
451 |
+
|
452 |
+
### Quick & Dirty
|
453 |
+
|
454 |
+
```php
|
455 |
+
require_once('path/to/php-doofinder/autoload.php');
|
456 |
+
|
457 |
+
define('API_KEY', 'eu1-384fdag73c7ff0a59g589xf9f4083bxb9727f9c3')
|
458 |
+
|
459 |
+
// Instantiate the object, use your doofinder's API_KEY.
|
460 |
+
$client = new \Doofinder\Api\Management\Client(API_KEY);
|
461 |
+
|
462 |
+
// Get a list of search engines
|
463 |
+
$searchEngines = $client->getSearchEngines();
|
464 |
+
// From the list, we will choose the first one
|
465 |
+
$mySearchEngine = $searchEngines[0];
|
466 |
+
```
|
467 |
+
|
468 |
+
The `SearchEngine` object gives you methods to manage a search engine.
|
469 |
+
|
470 |
+
#### Types Management
|
471 |
+
|
472 |
+
```php
|
473 |
+
$types = $mySearchEngine->getTypes(); // Obtain search engine's datatypes
|
474 |
+
$new_types = $mySearchEngine->addType('product'); // Add new type
|
475 |
+
$mySearchEngine->deleteType('product'); // Remove the type and all items within it.
|
476 |
+
```
|
477 |
+
|
478 |
+
#### Items Management
|
479 |
+
|
480 |
+
##### Single Item
|
481 |
+
|
482 |
+
```php
|
483 |
+
// Obtain item info within 'product' type
|
484 |
+
$item = $mySearchEngine->getItem('product', '888493');
|
485 |
+
// Add a new item to the 'product' type
|
486 |
+
$added_item = $mySearchEngine->addItem('product', array('id'=> 'newid', 'title'=>'a title'));
|
487 |
+
// Remove item
|
488 |
+
$mySearchEngine->deleteItem('product', 'newid');
|
489 |
+
// Update the '888493' item belonging to the 'product' type.
|
490 |
+
$mySearchEngine->updateItem('product', '888493', array('title'=>'modifiled title'));
|
491 |
+
```
|
492 |
+
|
493 |
+
##### Bulk Add/Update/Delete
|
494 |
+
|
495 |
+
```php
|
496 |
+
$mySearchEngine->updateItems('product', array(
|
497 |
+
array('title' => 'first item', 'id' => 'id1'),
|
498 |
+
array('title' => 'second item', 'id' => 'id2'),
|
499 |
+
));
|
500 |
+
|
501 |
+
$mySearchEngine->addItems('product', array(
|
502 |
+
array('title' => 'first item', 'id' => 'newid1'),
|
503 |
+
array('title' => 'second item'),
|
504 |
+
));
|
505 |
+
|
506 |
+
$mySearchEngine->deleteItems('product', array('id1', 'id2'));
|
507 |
+
```
|
508 |
+
|
509 |
+
##### Iterating Items
|
510 |
+
|
511 |
+
If you want to go through **every item in your index** you can only do it forwards. To do that, you'll need to use the *listing/scrolling* method…
|
512 |
+
|
513 |
+
__WARNING:__ You won't be iterating a standard PHP array. Items will be provided as an iterator object instance.
|
514 |
+
|
515 |
+
```php
|
516 |
+
$items = $mySearchEngine->items('product');
|
517 |
+
foreach ($items as $item) {
|
518 |
+
echo $item['title'];
|
519 |
+
}
|
520 |
+
```
|
521 |
+
|
522 |
+
You can't retrieve a specific item by index:
|
523 |
+
|
524 |
+
```php
|
525 |
+
$items = $mySearchEngine->items('product');
|
526 |
+
$item = $items[4]; // WRONG!!!!!
|
527 |
+
PHP Fatal error: Cannot use object of type ItemsRS as array…
|
528 |
+
```
|
529 |
+
|
530 |
+
#### Stats
|
531 |
+
|
532 |
+
```php
|
533 |
+
// Some PHP versions may need this
|
534 |
+
date_default_timezone_set('America/Havana');
|
535 |
+
|
536 |
+
// If not $from_date or $to_date provided, default is last 15 days
|
537 |
+
$from_date = new DateTime("2011-01-07");
|
538 |
+
$to_date = new DateTime("2011-02-07");
|
539 |
+
|
540 |
+
foreach ($mySearchEngine->stats($from_date, $to_date) as $key => $aggregated){
|
541 |
+
echo $aggregated['date']; // date of the aggregated data
|
542 |
+
echo $aggregated['clicked']; // # clicks in search results
|
543 |
+
echo $aggregated['searches']; // # complete searches. i.e.: "mp3 player"
|
544 |
+
echo $aggregated['api']; // # requests made through our API
|
545 |
+
echo $aggregated['parser']; // # requests used in feeds parsing
|
546 |
+
// (1 per each 100 parsed items)
|
547 |
+
echo $aggregated['queries']; // # "raw" search requests
|
548 |
+
// i.e.: "mp3", "mp3 p", "mp3 pl" ..
|
549 |
+
echo $aggregated['requests']; // total # of requests for that day
|
550 |
+
}
|
551 |
+
|
552 |
+
$top_clicked = $mySearchEngine->topTerms('clicked', $from_date, $to_date);
|
553 |
+
|
554 |
+
foreach ($top_clicked as $key => $clicked) {
|
555 |
+
echo $clicked['term']; // title of the clicked item
|
556 |
+
echo $clicked['count']; // # of clicks on that item
|
557 |
+
}
|
558 |
+
|
559 |
+
$top_searches = $mySearchEngine->topTerms('searches', $from_date, $to_date);
|
560 |
+
|
561 |
+
foreach ($top_searches as $key => $search) {
|
562 |
+
echo $search['term']; // search terms used
|
563 |
+
echo $search['count']; // # of times it's been used
|
564 |
+
}
|
565 |
+
|
566 |
+
$top_opportunities = $mySearchEngine->topTerms('opportunities', $from_date, $to_date);
|
567 |
+
|
568 |
+
foreach ($top_opportunities as $key => $opportunity) {
|
569 |
+
echo $opportunity['term']; // search terms used (that haven't yielded any result)
|
570 |
+
echo $opportunity['count']; // # of times it's been used
|
571 |
+
}
|
572 |
+
```
|
573 |
+
|
574 |
+
#### Tasks management
|
575 |
+
|
576 |
+
```php
|
577 |
+
// Ask our server to process a search engine's feeds
|
578 |
+
$taskResult = $mySearchEngine->process();
|
579 |
+
// Retrieve info about the last or current process of the search engine
|
580 |
+
$taskInfo = $mySearchEngine->processInfo();
|
581 |
+
// Retrieve info about a certain task
|
582 |
+
$taskInfo = $mySearchEngine->taskInfo($taskResult['task_id']);
|
583 |
+
// Get log info about the last processes
|
584 |
+
$logs = $mySearchEngine->logs();
|
585 |
+
```
|
586 |
+
|
587 |
+
## Run Tests
|
588 |
+
|
589 |
+
To run tests.
|
590 |
+
|
591 |
+
- Make sure you have (phpunit) [https://phpunit.de/] (version 4.8) and (php-mock-phpunit)[https://github.com/php-mock/php-mock-phpunit] (version 1.1.*). If you have composer you can run
|
592 |
+
````shell
|
593 |
+
$ composer.phar install
|
594 |
+
````
|
595 |
+
|
596 |
+
- Run the tests!!
|
597 |
+
````shell
|
598 |
+
$ phpunit
|
599 |
+
````
|
lib/php-doofinder/autoload.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
spl_autoload_register('autoload_doofinder_classes');
|
3 |
+
|
4 |
+
function autoload_doofinder_classes($className) {
|
5 |
+
$libraryPrefix = 'Doofinder\\Api\\';
|
6 |
+
$libraryDirectory = __DIR__ . '/src/';
|
7 |
+
|
8 |
+
$len = strlen($libraryPrefix);
|
9 |
+
|
10 |
+
// Binary safe comparison of $len first characters
|
11 |
+
if (strncmp($libraryPrefix, $className, $len) !== 0) {
|
12 |
+
return;
|
13 |
+
}
|
14 |
+
|
15 |
+
$classPath = str_replace('\\', '/', substr($className, $len)) . '.php';
|
16 |
+
$file = $libraryDirectory . $classPath;
|
17 |
+
|
18 |
+
if (file_exists($file)) {
|
19 |
+
require $file;
|
20 |
+
}
|
21 |
+
}
|
lib/php-doofinder/composer.json
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "doofinder/doofinder",
|
3 |
+
"type": "library",
|
4 |
+
"description": "Doofinder PHP API Client",
|
5 |
+
"keywords": ["search", "api", "doofinder"],
|
6 |
+
"homepage": "https://github.com/doofinder/php-doofinder",
|
7 |
+
"license": "MIT",
|
8 |
+
"authors": [
|
9 |
+
{
|
10 |
+
"name": "JoeZ99",
|
11 |
+
"email": "jzarate@gmail.com",
|
12 |
+
"role": "Developer"
|
13 |
+
}
|
14 |
+
],
|
15 |
+
"require": {
|
16 |
+
"php": ">=5.3.0"
|
17 |
+
},
|
18 |
+
"require-dev": {
|
19 |
+
"phpunit/phpunit": "4.8.*",
|
20 |
+
"php-mock/php-mock-phpunit": "1.1.*"
|
21 |
+
},
|
22 |
+
"autoload": {
|
23 |
+
"psr-4": {
|
24 |
+
"Doofinder\\Api\\": "src/"
|
25 |
+
}
|
26 |
+
}
|
27 |
+
}
|
lib/php-doofinder/phpunit.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<phpunit colors="true">
|
3 |
+
<testsuites>
|
4 |
+
<testsuite name="Application Test Suite">
|
5 |
+
<directory>./src/Test/</directory>
|
6 |
+
</testsuite>
|
7 |
+
</testsuites>
|
8 |
+
</phpunit>
|
lib/php-doofinder/src/Management/AggregatesIterator.php
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\SearchEngine;
|
6 |
+
use Doofinder\Api\Management\ItemsResultSet;
|
7 |
+
|
8 |
+
class AggregatesIterator extends ItemsResultSet {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class to Iterate through SearchEngine's aggregated stats data for a certain period.
|
12 |
+
*/
|
13 |
+
protected $last_page = 0;
|
14 |
+
protected $searchParams = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @param SearchEngine $searchEngine
|
18 |
+
* @param DateTime $from_date . Starting date of the period. Default: 15 days ago
|
19 |
+
* @param DateTime $to_date. Ending date of the period. Default: today.
|
20 |
+
*/
|
21 |
+
public function __construct(SearchEngine $searchEngine, $from_date = null, $to_date = null){
|
22 |
+
$this->last_page = 0;
|
23 |
+
|
24 |
+
if (!is_null($from_date)) {
|
25 |
+
$this->searchParams['from'] = $from_date->format("Ymd");
|
26 |
+
}
|
27 |
+
if (!is_null($to_date)) {
|
28 |
+
$this->searchParams['to'] = $to_date->format("Ymd");
|
29 |
+
}
|
30 |
+
|
31 |
+
parent::__construct($searchEngine);
|
32 |
+
}
|
33 |
+
|
34 |
+
protected function fetchResultsAndTotal() {
|
35 |
+
$params = $this->last_page > 0 ? array("page" => $this->last_page + 1) : array();
|
36 |
+
|
37 |
+
try{
|
38 |
+
$apiResponse = $this->searchEngine->client->managementApiCall(
|
39 |
+
'GET',
|
40 |
+
"{$this->searchEngine->hashid}/stats",
|
41 |
+
array_merge($params, $this->searchParams)
|
42 |
+
);
|
43 |
+
|
44 |
+
$this->resultsPage = $apiResponse['response']['aggregates'];
|
45 |
+
$this->total = $apiResponse['response']['count'];
|
46 |
+
$this->last_page++;
|
47 |
+
$this->currentItem = each($this->resultsPage);
|
48 |
+
} catch (NotFound $nfe) {
|
49 |
+
$this->resultsPage = array();
|
50 |
+
}
|
51 |
+
|
52 |
+
reset($this->resultsPage);
|
53 |
+
}
|
54 |
+
|
55 |
+
public function rewind() {
|
56 |
+
$this->last_page = 0;
|
57 |
+
parent::rewind();
|
58 |
+
}
|
59 |
+
}
|
lib/php-doofinder/src/Management/Client.php
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Author:: JoeZ99 (<jzarate@gmail.com>).
|
4 |
+
*
|
5 |
+
* License:: Apache License, Version 2.0
|
6 |
+
*
|
7 |
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
8 |
+
* not use this file except in compliance with the License. You may obtain
|
9 |
+
* a copy of the License at
|
10 |
+
*
|
11 |
+
* http://www.apache.org/licenses/LICENSE-2.0
|
12 |
+
*
|
13 |
+
* Unless required by applicable law or agreed to in writing, software
|
14 |
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
15 |
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
16 |
+
* License for the specific language governing permissions and limitations
|
17 |
+
* under the License.
|
18 |
+
*/
|
19 |
+
|
20 |
+
namespace Doofinder\Api\Management;
|
21 |
+
|
22 |
+
use Doofinder\Api\Management\SearchEngine;
|
23 |
+
use Doofinder\Api\Management\Errors\Utils;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Class to manage the connection with the API servers.
|
27 |
+
*
|
28 |
+
* Needs APIKEY in initialization.
|
29 |
+
* Example: $dma = new DoofinderManagementApi('eu1-d531af87f10969f90792a4296e2784b089b8a875')
|
30 |
+
*/
|
31 |
+
class Client
|
32 |
+
{
|
33 |
+
const REMOTE_API_ENDPOINT = "https://%s-api.doofinder.com/v1";
|
34 |
+
const LOCAL_API_ENDPOINT = "http://localhost:8000/api/v1";
|
35 |
+
|
36 |
+
private $apiKey = null;
|
37 |
+
private $clusterRegion = 'eu1';
|
38 |
+
private $token = null;
|
39 |
+
private $baseManagementUrl = null;
|
40 |
+
|
41 |
+
public function __construct($apiKey, $local = false) {
|
42 |
+
$this->apiKey = $apiKey;
|
43 |
+
$clusterToken = explode('-', $apiKey);
|
44 |
+
$this->clusterRegion = $clusterToken[0];
|
45 |
+
$this->token = $clusterToken[1];
|
46 |
+
|
47 |
+
if ($local === true){
|
48 |
+
$this->baseManagementUrl = self::LOCAL_API_ENDPOINT;
|
49 |
+
} else {
|
50 |
+
$this->baseManagementUrl = sprintf(self::REMOTE_API_ENDPOINT, $this->clusterRegion);
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Makes the actual request to the API server and normalize response
|
56 |
+
*
|
57 |
+
* @param string $method The HTTP method to use. 'GET|PUT|POST|DELETE'
|
58 |
+
* @param string $entryPoint The path to use. '/<hashid>/items/product'
|
59 |
+
* @param array $params If any, url request parameters
|
60 |
+
* @param array $data If any, body request parameters
|
61 |
+
* @return array Array with both status code and response .
|
62 |
+
*/
|
63 |
+
public function managementApiCall($method = 'GET', $entryPoint = '', $params = null, $data = null) {
|
64 |
+
$headers = array(
|
65 |
+
'Authorization: Token ' . $this->token,
|
66 |
+
'Content-Type: application/json',
|
67 |
+
'Expect:', // Fixes the HTTP/1.1 417 Expectation Failed error
|
68 |
+
);
|
69 |
+
|
70 |
+
$url = $this->baseManagementUrl.'/'.$entryPoint;
|
71 |
+
|
72 |
+
if(is_array($params) && sizeof($params) > 0){
|
73 |
+
$url .= '?'.http_build_query($params);
|
74 |
+
}
|
75 |
+
|
76 |
+
if (!in_array($method, array('POST', 'PUT', 'DELETE'))){
|
77 |
+
$data = null;
|
78 |
+
}
|
79 |
+
$serverResponse = $this->talkToServer($method, $url, $headers, $data);
|
80 |
+
$statusCode = $serverResponse['statusCode'];
|
81 |
+
$contentResponse = $serverResponse['contentResponse'];
|
82 |
+
|
83 |
+
$error = Utils::handleErrors($statusCode, $contentResponse);
|
84 |
+
|
85 |
+
if ($error) {
|
86 |
+
throw $error;
|
87 |
+
}
|
88 |
+
|
89 |
+
return array(
|
90 |
+
'statusCode' => $statusCode,
|
91 |
+
'response' => ($decoded = json_decode($contentResponse, true)) ? $decoded : $contentResponse
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* To get info on all possible api entry points
|
97 |
+
* @return array An assoc. array with the different entry points
|
98 |
+
*/
|
99 |
+
public function getApiRoot() {
|
100 |
+
$response = $this->managementApiCall();
|
101 |
+
return $response['response'];
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Obtain a list of SearchEngines objects, ready to interact with the API
|
106 |
+
* @return array list of searchEngines objects
|
107 |
+
*/
|
108 |
+
public function getSearchEngines(){
|
109 |
+
$searchEngines = array();
|
110 |
+
$apiRoot = $this->getApiRoot();
|
111 |
+
unset($apiRoot['searchengines']);
|
112 |
+
|
113 |
+
foreach ($apiRoot as $hashid => $props) {
|
114 |
+
$searchEngines[] = new SearchEngine($this, $hashid, $props['name']);
|
115 |
+
}
|
116 |
+
|
117 |
+
return $searchEngines;
|
118 |
+
}
|
119 |
+
|
120 |
+
protected function talkToServer($method, $url, $headers, $data)
|
121 |
+
{
|
122 |
+
$session = curl_init($url);
|
123 |
+
curl_setopt($session, CURLOPT_CUSTOMREQUEST, $method);
|
124 |
+
curl_setopt($session, CURLOPT_HEADER, false);
|
125 |
+
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Tell curl to return the response
|
126 |
+
curl_setopt($session, CURLOPT_HTTPHEADER, $headers); // Adding request headers
|
127 |
+
|
128 |
+
if (!is_null($data)) {
|
129 |
+
curl_setopt($session, CURLOPT_POSTFIELDS, $data);
|
130 |
+
}
|
131 |
+
|
132 |
+
$contentResponse = curl_exec($session);
|
133 |
+
$statusCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
134 |
+
|
135 |
+
curl_close($session);
|
136 |
+
|
137 |
+
return array(
|
138 |
+
'contentResponse' => $contentResponse,
|
139 |
+
'statusCode' => $statusCode
|
140 |
+
);
|
141 |
+
|
142 |
+
}
|
143 |
+
|
144 |
+
}
|
lib/php-doofinder/src/Management/Errors/BadRequest.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class BadRequest extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/NotAllowed.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class NotAllowed extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/NotFound.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Doofinder\Api\Management\Errors;
|
3 |
+
|
4 |
+
class NotFound extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/NotProcessedResponse.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class NotProcessedResponse extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/QuotaExhausted.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class QuotaExhausted extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/ThrottledResponse.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class ThrottledResponse extends \Exception {}
|
lib/php-doofinder/src/Management/Errors/Utils.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\Errors\NotAllowed;
|
6 |
+
use Doofinder\Api\Management\Errors\NotFound;
|
7 |
+
use Doofinder\Api\Management\Errors\BadRequest;
|
8 |
+
use Doofinder\Api\Management\Errors\ThrottledResponse;
|
9 |
+
use Doofinder\Api\Management\Errors\QuotaExhausted;
|
10 |
+
use Doofinder\Api\Management\Errors\WrongResponse;
|
11 |
+
|
12 |
+
|
13 |
+
class Utils {
|
14 |
+
public static function handleErrors($statusCode, $response) {
|
15 |
+
switch ($statusCode) {
|
16 |
+
case 403:
|
17 |
+
return new NotAllowed("The user does not have permissions to perform this operation: ".Utils::readError($response));
|
18 |
+
case 401:
|
19 |
+
return new NotAllowed("The user hasn't provided valid authorization: ".Utils::readError($response));
|
20 |
+
case 404:
|
21 |
+
return new NotFound("Not Found: ".Utils::readError($response));
|
22 |
+
case 409: // trying to post with an already used id
|
23 |
+
return new BadRequest("Request conflict: ".Utils::readError($response));
|
24 |
+
case 429:
|
25 |
+
if (stripos($response, 'throttled')) {
|
26 |
+
return new ThrottledResponse(Utils::readError($response));
|
27 |
+
} else {
|
28 |
+
return new QuotaExhausted("The query quota has been reached. No more queries can be requested right now");
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
if ($statusCode >= 500) {
|
33 |
+
return new WrongResponse("Server error: ".Utils::readError($response));
|
34 |
+
}
|
35 |
+
|
36 |
+
if ($statusCode >= 400) {
|
37 |
+
return new BadRequest("The client made a bad request: ".Utils::readError($response));
|
38 |
+
}
|
39 |
+
|
40 |
+
return false;
|
41 |
+
}
|
42 |
+
|
43 |
+
private static function readError($response) {
|
44 |
+
$error = json_decode($response, true);
|
45 |
+
$error = $error['detail'];
|
46 |
+
|
47 |
+
if (!isset($error)) {
|
48 |
+
$error = $response;
|
49 |
+
}
|
50 |
+
|
51 |
+
return $error;
|
52 |
+
}
|
53 |
+
}
|
lib/php-doofinder/src/Management/Errors/WrongResponse.php
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management\Errors;
|
4 |
+
|
5 |
+
class WrongResponse extends \Exception {}
|
lib/php-doofinder/src/Management/ItemsResultSet.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\SearchEngine;
|
6 |
+
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Helper class to iterate through the search engine's items
|
10 |
+
*
|
11 |
+
* Implemets Iterator interface so foreach() can work with ItemRS
|
12 |
+
* It's supposed to be extended
|
13 |
+
*/
|
14 |
+
class ItemsResultSet implements \Iterator {
|
15 |
+
|
16 |
+
protected $searchEngine = null;
|
17 |
+
protected $resultsPage = null;
|
18 |
+
protected $position = 0;
|
19 |
+
protected $total = null;
|
20 |
+
|
21 |
+
public function __construct(SearchEngine $searchEngine) {
|
22 |
+
$this->searchEngine = $searchEngine;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* To be implemented in children
|
27 |
+
*/
|
28 |
+
protected function fetchResultsAndTotal() {
|
29 |
+
throw new Exception('Not implemented.');
|
30 |
+
}
|
31 |
+
|
32 |
+
public function rewind() {
|
33 |
+
$this->position = 0;
|
34 |
+
$this->total = null;
|
35 |
+
$this->resultsPage = null;
|
36 |
+
$this->fetchResultsAndTotal();
|
37 |
+
$this->currentItem = each($this->resultsPage);
|
38 |
+
}
|
39 |
+
|
40 |
+
public function valid() {
|
41 |
+
return $this->position < $this->total;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function current() {
|
45 |
+
return $this->currentItem['value'];
|
46 |
+
}
|
47 |
+
|
48 |
+
public function key() {
|
49 |
+
return $this->position;
|
50 |
+
}
|
51 |
+
|
52 |
+
public function next() {
|
53 |
+
++$this->position;
|
54 |
+
$this->currentItem = each($this->resultsPage);
|
55 |
+
if (!$this->currentItem && $this->position < $this->total) {
|
56 |
+
$this->fetchResultsAndTotal();
|
57 |
+
$this->currentItem = each($this->resultsPage);
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
lib/php-doofinder/src/Management/ScrollIterator.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\SearchEngine;
|
6 |
+
use \Doofinder\Api\Management\ItemsResultSet;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* ScrollIterator
|
10 |
+
*
|
11 |
+
* Class to Iterate/Scroll through search engine's indexed items of a certain datatype
|
12 |
+
*/
|
13 |
+
class ScrollIterator extends ItemsResultSet {
|
14 |
+
|
15 |
+
private $scrollId = null;
|
16 |
+
private $datatype = null;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @param SearchEngine $searchEngine
|
20 |
+
* @param string $datatype type of item . i.e. 'product'
|
21 |
+
*/
|
22 |
+
public function __construct(SearchEngine $searchEngine, $datatype){
|
23 |
+
$this->datatype = $datatype;
|
24 |
+
parent::__construct($searchEngine);
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Loads net next batch of api results
|
29 |
+
*
|
30 |
+
*/
|
31 |
+
protected function fetchResultsAndTotal(){
|
32 |
+
$apiResults = $this->searchEngine->client->managementApiCall(
|
33 |
+
'GET',
|
34 |
+
"{$this->searchEngine->hashid}/items/{$this->datatype}",
|
35 |
+
($this->scrollId ? array("scroll_id" => $this->scrollId) : null)
|
36 |
+
);
|
37 |
+
$this->total = $apiResults['response']['count'];
|
38 |
+
$this->scrollId = $apiResults['response']['scroll_id'];
|
39 |
+
$this->resultsPage = $apiResults['response']['results'];
|
40 |
+
$this->currentItem = each($this->resultsPage);
|
41 |
+
|
42 |
+
reset($this->resultsPage);
|
43 |
+
}
|
44 |
+
|
45 |
+
public function rewind(){
|
46 |
+
$this->scrollId = null;
|
47 |
+
parent::rewind();
|
48 |
+
}
|
49 |
+
}
|
lib/php-doofinder/src/Management/SearchEngine.php
ADDED
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class with all the capabilities described in the API
|
4 |
+
*
|
5 |
+
* see http://www.doofinder.com/developer/topics/api/management-api
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Doofinder\Api\Management;
|
9 |
+
|
10 |
+
use Doofinder\Api\Management\Client;
|
11 |
+
use Doofinder\Api\Management\Errors\BadRequest;
|
12 |
+
use Doofinder\Api\Management\TopTermsIterator;
|
13 |
+
use Doofinder\Api\Management\ScrollIterator;
|
14 |
+
use Doofinder\Api\Management\AggregatesIterator;
|
15 |
+
|
16 |
+
|
17 |
+
class SearchEngine {
|
18 |
+
public $name = null;
|
19 |
+
public $hashid = null;
|
20 |
+
public $client = null;
|
21 |
+
|
22 |
+
public function __construct(Client $client, $hashid, $name) {
|
23 |
+
$this->name = $name;
|
24 |
+
$this->hashid = $hashid;
|
25 |
+
$this->client = $client;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Get a list of searchengine's types
|
30 |
+
*
|
31 |
+
* @return array list of types
|
32 |
+
*/
|
33 |
+
public function getDatatypes() {
|
34 |
+
return $this->getTypes();
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Get a list of searchengine's types
|
39 |
+
*
|
40 |
+
* @return array list of types
|
41 |
+
*/
|
42 |
+
public function getTypes() {
|
43 |
+
$result = $this->client->managementApiCall('GET', "{$this->hashid}/types");
|
44 |
+
return $result['response'];
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Add a type to the searchengine
|
49 |
+
*
|
50 |
+
* @param string $datatype the type name
|
51 |
+
* @return new list of searchengine's types
|
52 |
+
*/
|
53 |
+
public function addType($datatype) {
|
54 |
+
$result = $this->client->managementApiCall('POST', "{$this->hashid}/types", null, json_encode(array('name' => $datatype)));
|
55 |
+
return $result['response'];
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Delete a type and all its items. HANDLE WITH CARE
|
60 |
+
*
|
61 |
+
* @param string $datatype the Type to delete. All items belonging
|
62 |
+
* to that type will be removed. mandatory
|
63 |
+
* @return boolean true on success
|
64 |
+
*/
|
65 |
+
public function deleteType($datatype) {
|
66 |
+
$result = $this->client->managementApiCall('DELETE', "{$this->hashid}/types/{$datatype}");
|
67 |
+
return $result['statusCode'] == 204;
|
68 |
+
}
|
69 |
+
|
70 |
+
public function items($datatype) {
|
71 |
+
return new ScrollIterator($this, $datatype);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Get details of a specific item
|
76 |
+
*
|
77 |
+
* @param string $datatype Type of the item.
|
78 |
+
* @param string $itemId the id of the item
|
79 |
+
* @return array Assoc array representing the item.
|
80 |
+
*/
|
81 |
+
public function getItem($datatype, $itemId) {
|
82 |
+
$result = $this->client->managementApiCall('GET', "{$this->hashid}/items/{$datatype}/{$itemId}");
|
83 |
+
return $result['response'];
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Add an item to the search engine
|
88 |
+
*
|
89 |
+
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
90 |
+
* item with that id.
|
91 |
+
* - It the 'id' field is not present, create one.
|
92 |
+
*
|
93 |
+
* @param string $datatype type of the. If not provided, first available type is used
|
94 |
+
* @param array $itemDescription Assoc array representation of the item
|
95 |
+
* @return string the id of the item just created
|
96 |
+
*/
|
97 |
+
public function addItem($datatype, $itemDescription) {
|
98 |
+
$result = $this->client->managementApiCall('POST', "{$this->hashid}/items/{$datatype}", null, json_encode($itemDescription));
|
99 |
+
return $result['response']['id'];
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Add items in bulk to the search engine
|
104 |
+
*
|
105 |
+
* For each item:
|
106 |
+
* - If the 'id' field is present, use that as item's id or overwrite an existing
|
107 |
+
* item with that id.
|
108 |
+
* - It the 'id' field is not present, create one.
|
109 |
+
*
|
110 |
+
* @param string $datatype type of the. If not provided, first available type is used
|
111 |
+
* @param array $itemsDescription List of Assoc array representation of the item
|
112 |
+
* @return array List of ids of the added items
|
113 |
+
*/
|
114 |
+
public function addItems($datatype, $itemsDescription) {
|
115 |
+
$result = $this->client->managementApiCall('POST', "{$this->hashid}/items/{$datatype}", null, json_encode($itemsDescription));
|
116 |
+
|
117 |
+
return array_map(function($it){return $it['id'];}, $result['response']);
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Update or create an item of the search engine
|
122 |
+
*
|
123 |
+
* In case of conflict between itemDescription's id or $itemId,
|
124 |
+
* the latter is used.
|
125 |
+
*
|
126 |
+
* @param string $datatype type of the Item.
|
127 |
+
* @param string $itemId Id of the item to be updated/added
|
128 |
+
* @param array $itemDescription Assoc array representating the item.
|
129 |
+
* @return boolean true on success.
|
130 |
+
*/
|
131 |
+
public function updateItem($datatype, $itemId, $itemDescription) {
|
132 |
+
$result = $this->client->managementApiCall('PUT', "{$this->hashid}/items/{$datatype}/{$itemId}", null, json_encode($itemDescription));
|
133 |
+
return $result['statusCode'] == 200;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Bulk update of several items
|
138 |
+
*
|
139 |
+
* Each item description must contain the 'id' field
|
140 |
+
*
|
141 |
+
* @param string $datatype type of the items.
|
142 |
+
* @param array $itemsDescription List of assoc array representing items
|
143 |
+
* @return boolean true on success
|
144 |
+
*/
|
145 |
+
public function updateItems($datatype, $itemsDescription) {
|
146 |
+
$result = $this->client->managementApiCall('PUT', "{$this->hashid}/items/{$datatype}", null, json_encode($itemsDescription));
|
147 |
+
return $result['statusCode'] == 200;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Delete an item
|
152 |
+
*
|
153 |
+
* @param string $datatype type of the item
|
154 |
+
* @param string $itemId id of the item
|
155 |
+
* @return boolean true if success, false if failure
|
156 |
+
*/
|
157 |
+
public function deleteItem($datatype, $itemId) {
|
158 |
+
$result = $this->client->managementApiCall('DELETE', "{$this->hashid}/items/{$datatype}/{$itemId}");
|
159 |
+
return $result['statusCode'] == 204 ;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Delete items in bulk (up to 100)
|
164 |
+
*
|
165 |
+
* @param string $datatype type of the items
|
166 |
+
* @param array list of item ids to be deleted
|
167 |
+
* @return array assoc array with both items which could be deleted and couldn't
|
168 |
+
* array('errors'=>array(), 'success'=>array('AX01', 'AX02', 'AXFD')
|
169 |
+
*/
|
170 |
+
public function deleteItems($datatype, $itemsIds) {
|
171 |
+
$objects = array_map(function($id){return array('id'=>$id);}, $itemsIds);
|
172 |
+
$result = $this->client->managementApiCall(
|
173 |
+
'DELETE', "{$this->hashid}/items/{$datatype}", null, json_encode($objects)
|
174 |
+
);
|
175 |
+
return $result['response'];
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Obtain stats aggregated data for a certain period.
|
180 |
+
*
|
181 |
+
* @param DateTime $from_date. Starting date. Default is 15 days ago
|
182 |
+
* @param DateTime $to_date. Ending date. Default is today
|
183 |
+
*
|
184 |
+
* @return ItemsRS iterator through daily aggregated data.
|
185 |
+
*/
|
186 |
+
public function stats($from_date = null, $to_date = null) {
|
187 |
+
return new AggregatesIterator($this, $from_date, $to_date);
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Obtain frequency sorted list of therms used for a certain period.
|
192 |
+
*
|
193 |
+
* @param string term: type of term 'clicked', 'searches', 'opportunities'
|
194 |
+
* - 'clicked': clicked items
|
195 |
+
* - 'searches': complete searches
|
196 |
+
* - 'opportunities': searches without results
|
197 |
+
* @param DateTime $from_date. Starting date. Default is 15 days ago
|
198 |
+
* @param DateTime $to_date. Ending date. Default is today
|
199 |
+
*
|
200 |
+
* @return ItemsRS iterator through terms stats.
|
201 |
+
*/
|
202 |
+
public function topTerms($term, $from_date = null, $to_date = null) {
|
203 |
+
if (!in_array($term, array('clicked', 'searches', 'opportunities'))) {
|
204 |
+
throw new BadRequest("The term {$term} is not allowed");
|
205 |
+
}
|
206 |
+
|
207 |
+
return new TopTermsIterator($this, $term, $from_date, $to_date);
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Ask the server to process the search engine's feeds
|
212 |
+
*
|
213 |
+
* @return array Assoc array with:
|
214 |
+
* - 'task_created': boolean true if a new task has been created
|
215 |
+
* - 'task_id': if task created, the id of the task.
|
216 |
+
*/
|
217 |
+
public function process() {
|
218 |
+
$result = $this->client->managementApiCall('POST', "{$this->hashid}/tasks/process");
|
219 |
+
$taskCreated = ($result['statusCode'] == 201);
|
220 |
+
$taskId = $taskCreated ? SearchEngine::obtainId($result['response']['link']) : null;
|
221 |
+
|
222 |
+
return array('task_created' => $taskCreated, 'task_id' => $taskId);
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Obtain info of the last processing task sent to the server
|
227 |
+
*
|
228 |
+
* @return array Assoc array with 'state' and 'message' indicating status of the
|
229 |
+
* last asked processing task
|
230 |
+
*/
|
231 |
+
public function processInfo() {
|
232 |
+
$result = $this->client->managementApiCall('GET', "{$this->hashid}/tasks/process");
|
233 |
+
unset($result['response']['task_name']);
|
234 |
+
return $result['response'];
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Obtain info about how a task is going or its result
|
239 |
+
*
|
240 |
+
* @return array Assoc array with 'state' and 'message' indicating the status
|
241 |
+
* of the task
|
242 |
+
*/
|
243 |
+
public function taskInfo($taskId) {
|
244 |
+
$result = $this->client->managementApiCall('GET', "{$this->hashid}/tasks/{$taskId}");
|
245 |
+
unset($result['response']['task_name']);
|
246 |
+
return $result['response'];
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Obtain logs of the latest feed processing tasks done
|
251 |
+
*
|
252 |
+
* @return array list of arrays representing the logs
|
253 |
+
*/
|
254 |
+
public function logs() {
|
255 |
+
$result = $this->client->managementApiCall("GET", "{$this->hashid}/logs");
|
256 |
+
return $result['response'];
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Extracts identificator from an item or task url.
|
261 |
+
*
|
262 |
+
* @param string $url item or task resource locator
|
263 |
+
* @return the item identificator
|
264 |
+
*/
|
265 |
+
private static function obtainId($url) {
|
266 |
+
preg_match('~/\w{32}/(items/\w+|tasks)/([\w-_]+)/?$~', $url, $matches);
|
267 |
+
return $matches[2];
|
268 |
+
}
|
269 |
+
}
|
lib/php-doofinder/src/Management/TopTermsIterator.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\SearchEngine;
|
6 |
+
use Doofinder\Api\Management\AggregatesIterator;
|
7 |
+
use Doofinder\Api\Management\Errors\NotProcessedResponse;
|
8 |
+
use Doofinder\Api\Management\Errors\MotFound;
|
9 |
+
|
10 |
+
class TopTermsIterator extends AggregatesIterator {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Class to Iterate through SearchEngine's top terms stats data for a certain period.
|
14 |
+
*/
|
15 |
+
private $term = null; // type of term: 'clicked', 'searches', 'opportunities'
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Constructor
|
19 |
+
*
|
20 |
+
* @param SearchEngine $searchEngine
|
21 |
+
* @param DateTime $from_date . Starting date of the period. Default: 15 days ago
|
22 |
+
* @param DateTime $to_date. Ending date of the period. Default: today.
|
23 |
+
* @param string term. type of term: 'clicked', 'searches', 'opportunities'
|
24 |
+
*/
|
25 |
+
public function __construct(SearchEngine $searchEngine, $term, $from_date = null, $to_date = null) {
|
26 |
+
$this->term = $term;
|
27 |
+
parent::__construct($searchEngine, $from_date, $to_date);
|
28 |
+
}
|
29 |
+
|
30 |
+
protected function fetchResultsAndTotal() {
|
31 |
+
$params = $this->last_page > 0 ? array("page" => $this->last_page + 1) : array();
|
32 |
+
try {
|
33 |
+
$apiResponse = $this->searchEngine->client->managementApiCall(
|
34 |
+
'GET',
|
35 |
+
"{$this->searchEngine->hashid}/stats/top_{$this->term}",
|
36 |
+
array_merge($params, $this->searchParams)
|
37 |
+
);
|
38 |
+
|
39 |
+
// still generating?
|
40 |
+
if ($apiResponse['statusCode'] == 202) {
|
41 |
+
throw new NotProcessedResponse("Your request is still being processed. Please try again later");
|
42 |
+
}
|
43 |
+
|
44 |
+
$this->resultsPage = $apiResponse['response'][$this->term];
|
45 |
+
$this->total = $apiResponse['response']['count'];
|
46 |
+
$this->last_page++;
|
47 |
+
$this->currentItem = each($this->resultsPage);
|
48 |
+
} catch (NotFound $nfe) {
|
49 |
+
$this->resultsPage = array();
|
50 |
+
}
|
51 |
+
|
52 |
+
reset($this->resultsPage);
|
53 |
+
}
|
54 |
+
}
|
lib/php-doofinder/src/Search/Client.php
ADDED
@@ -0,0 +1,581 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Author:: JoeZ99 (<jzarate@gmail.com>).
|
4 |
+
* Author:: @carlosescri (http://github.com/carlosescri)
|
5 |
+
*
|
6 |
+
* All credit goes to Gilles Devaux (<gilles.devaux@gmail.com>)
|
7 |
+
* (https://github.com/flaptor/indextank-php)
|
8 |
+
*
|
9 |
+
* License:: Apache License, Version 2.0
|
10 |
+
*
|
11 |
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
12 |
+
* not use this file except in compliance with the License. You may obtain
|
13 |
+
* a copy of the License at
|
14 |
+
*
|
15 |
+
* http://www.apache.org/licenses/LICENSE-2.0
|
16 |
+
*
|
17 |
+
* Unless required by applicable law or agreed to in writing, software
|
18 |
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
19 |
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
20 |
+
* License for the specific language governing permissions and limitations
|
21 |
+
* under the License.
|
22 |
+
*/
|
23 |
+
|
24 |
+
namespace Doofinder\Api\Search;
|
25 |
+
|
26 |
+
use Doofinder\Api\Search\Results;
|
27 |
+
use Doofinder\Api\Search\Error;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Doofinder\Api\Client
|
31 |
+
*
|
32 |
+
* Basic client to perform requests to Doofinder search servers.
|
33 |
+
*/
|
34 |
+
class Client {
|
35 |
+
const URL_SUFFIX = '-search.doofinder.com';
|
36 |
+
const DEFAULT_TIMEOUT = 10000;
|
37 |
+
const DEFAULT_RPP = 10;
|
38 |
+
const DEFAULT_PARAMS_PREFIX = 'dfParam_';
|
39 |
+
const DEFAULT_API_VERSION = '5';
|
40 |
+
const VERSION = '5.4.3';
|
41 |
+
|
42 |
+
private $api_key = null; // Authentication
|
43 |
+
private $hashid = null; // ID of the Search Engine
|
44 |
+
|
45 |
+
private $apiVersion = null;
|
46 |
+
private $url = null;
|
47 |
+
private $results = null;
|
48 |
+
private $query = null;
|
49 |
+
private $search_options = array(); // Assoc. array of request parameters
|
50 |
+
|
51 |
+
private $page = 1; // Current "page" of results
|
52 |
+
private $queryName = null; // Last successful query name
|
53 |
+
private $lastQuery = null; // Last successful query made
|
54 |
+
private $total = null; // Total number of results
|
55 |
+
private $maxScore = null;
|
56 |
+
private $paramsPrefix = self::DEFAULT_PARAMS_PREFIX;
|
57 |
+
private $serializationArray = null;
|
58 |
+
private $queryParameter = 'query';
|
59 |
+
private $allowedParameters = array('page', 'rpp', 'timeout', 'types',
|
60 |
+
'filter', 'query_name', 'transformer',
|
61 |
+
'sort'); // Valid parameters
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Constructor.
|
65 |
+
*
|
66 |
+
* @param string $hashid Search Engine's hashid.
|
67 |
+
* @param boolean $fromParams If set, the object is unserialized from GET
|
68 |
+
* or POST params.
|
69 |
+
* @param array $init_options Associative array with some options:
|
70 |
+
*
|
71 |
+
* -'prefix' (default: 'dfParam_') Prefix to use when serializing.
|
72 |
+
* -'queryParameter' (default: 'query') Parameter used for querying.
|
73 |
+
* -'apiVersion' (default: '5') Version of the API to use.
|
74 |
+
* Valid versions: 5, 4, 3.0, 1.0
|
75 |
+
* -'restrictedRequest' (default: null) Can be 'get' or 'post'. If defined, restricts
|
76 |
+
* the request object to either $_GET or $_POST
|
77 |
+
* when unserializing to get parameters.
|
78 |
+
*
|
79 |
+
* @throws Error if $hashid is not a md5 hash or API version is not valid.
|
80 |
+
*/
|
81 |
+
public function __construct($hashid, $api_key, $fromParams = false, $init_options = array()) {
|
82 |
+
$zone_key_array = explode('-', $api_key);
|
83 |
+
|
84 |
+
if (2 === count($zone_key_array)) {
|
85 |
+
$this->api_key = $zone_key_array[1];
|
86 |
+
$this->zone = $zone_key_array[0];
|
87 |
+
$this->url = "https://" . $this->zone . self::URL_SUFFIX;
|
88 |
+
} else {
|
89 |
+
throw new Error("API Key is no properly set.");
|
90 |
+
}
|
91 |
+
|
92 |
+
if (array_key_exists('prefix', $init_options)) {
|
93 |
+
$this->paramsPrefix = $init_options['prefix'];
|
94 |
+
}
|
95 |
+
|
96 |
+
$this->allowedParameters = array_map(array($this, 'addPrefix'), $this->allowedParameters);
|
97 |
+
|
98 |
+
if (array_key_exists('queryParameter', $init_options)) {
|
99 |
+
$this->queryParameter = $init_options['queryParameter'];
|
100 |
+
} else {
|
101 |
+
$this->queryParameter = $this->paramsPrefix.$this->queryParameter;
|
102 |
+
}
|
103 |
+
|
104 |
+
if (array_key_exists('apiVersion', $init_options)) {
|
105 |
+
$this->setApiVersion($init_options['apiVersion']);
|
106 |
+
} else {
|
107 |
+
$this->setApiVersion(self::DEFAULT_API_VERSION);
|
108 |
+
}
|
109 |
+
|
110 |
+
if (array_key_exists('restrictedRequest', $init_options)) {
|
111 |
+
switch(strtolower($init_options['restrictedRequest'])) {
|
112 |
+
case 'get':
|
113 |
+
$this->serializationArray = $_GET;
|
114 |
+
break;
|
115 |
+
case 'post':
|
116 |
+
$this->serializationArray = $_POST;
|
117 |
+
break;
|
118 |
+
default:
|
119 |
+
throw new Error("Wrong initialization value for 'restrictedRequest'");
|
120 |
+
}
|
121 |
+
} else {
|
122 |
+
$this->serializationArray = $_REQUEST;
|
123 |
+
}
|
124 |
+
|
125 |
+
if (!preg_match('/^[0-9a-f]{32}$/i', $hashid)) {
|
126 |
+
throw new Error("Wrong hashid");
|
127 |
+
}
|
128 |
+
|
129 |
+
$this->hashid = $hashid;
|
130 |
+
|
131 |
+
if ($fromParams) {
|
132 |
+
$this->fromQuerystring();
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
private function addPrefix($value) {
|
137 |
+
return $this->paramsPrefix.$value;
|
138 |
+
}
|
139 |
+
|
140 |
+
private function getRequestHeaders(){
|
141 |
+
$headers = array();
|
142 |
+
$headers[] = 'Expect:'; // Fixes HTTP/1.1 "417 Expectation Failed" Error
|
143 |
+
if ($this->authenticationHeader !== false) {
|
144 |
+
$headers[] = sprintf("%s: %s", $this->authenticationHeader, $this->api_key);
|
145 |
+
}
|
146 |
+
return $headers;
|
147 |
+
}
|
148 |
+
|
149 |
+
private function apiCall($entry_point = 'search', $params = array()){
|
150 |
+
$params['hashid'] = $this->hashid;
|
151 |
+
|
152 |
+
$session = curl_init(sprintf("%s/%s/%s?%s", $this->url,
|
153 |
+
$this->apiVersion,
|
154 |
+
$entry_point,
|
155 |
+
http_build_query($this->sanitize($params))));
|
156 |
+
|
157 |
+
// Configure cURL to return response but not headers
|
158 |
+
curl_setopt($session, CURLOPT_CUSTOMREQUEST, 'GET');
|
159 |
+
curl_setopt($session, CURLOPT_HEADER, false);
|
160 |
+
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
|
161 |
+
curl_setopt($session, CURLOPT_HTTPHEADER, $this->getRequestHeaders());
|
162 |
+
|
163 |
+
$response = curl_exec($session);
|
164 |
+
$statusCode = curl_getinfo($session, CURLINFO_HTTP_CODE);
|
165 |
+
|
166 |
+
curl_close($session);
|
167 |
+
|
168 |
+
if (floor($statusCode / 100) == 2) {
|
169 |
+
return $response;
|
170 |
+
}
|
171 |
+
|
172 |
+
throw new Error($statusCode.' - '.$response, $statusCode);
|
173 |
+
}
|
174 |
+
|
175 |
+
public function getOptions() {
|
176 |
+
return $this->apiCall('options/'.$this->hashid);
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* query. makes the query to the doofinder search server.
|
181 |
+
* also set several search parameters through it's $options argument
|
182 |
+
*
|
183 |
+
* @param string $query the search query
|
184 |
+
* @param int $page the page number or the results to show
|
185 |
+
* @param array $options query options:
|
186 |
+
* - 'rpp'=> number of results per page. default 10
|
187 |
+
* - 'timeout' => timeout after which the search server drops the conn.
|
188 |
+
* defaults to 10 seconds
|
189 |
+
* - 'types' => types of index to search at. default: all.
|
190 |
+
* - 'filter' => filter to apply. ['color'=>['red','blue'], 'price'=>['from'=>33]]
|
191 |
+
* - any other param will be sent as a request parameter
|
192 |
+
* @return DoofinderResults results
|
193 |
+
*/
|
194 |
+
public function query($query = null, $page = null, $options = array()) {
|
195 |
+
if ($query) {
|
196 |
+
$this->search_options['query'] = $query;
|
197 |
+
}
|
198 |
+
|
199 |
+
if ($page) {
|
200 |
+
$this->search_options['page'] = intval($page);
|
201 |
+
}
|
202 |
+
|
203 |
+
foreach ($options as $optionName => $optionValue) {
|
204 |
+
$this->search_options[$optionName] = $options[$optionName];
|
205 |
+
}
|
206 |
+
|
207 |
+
$params = $this->search_options;
|
208 |
+
|
209 |
+
// translate filters
|
210 |
+
if(!empty($params['filter']))
|
211 |
+
{
|
212 |
+
foreach($params['filter'] as $filterName => $filterValue){
|
213 |
+
$params['filter'][$filterName] = $this->updateFilter($filterValue);
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
// no query? then match all documents
|
218 |
+
if(!$this->optionExists('query') || !trim($this->search_options['query'])){
|
219 |
+
$params['query_name'] = 'match_all';
|
220 |
+
}
|
221 |
+
|
222 |
+
// if filters without query_name, pre-query first to obtain it.
|
223 |
+
if (empty($params['query_name']) && !empty($params['filter']))
|
224 |
+
{
|
225 |
+
$filter = $params['filter'];
|
226 |
+
unset($params['filter']);
|
227 |
+
$dfResults = new Results($this->apiCall('search', $params));
|
228 |
+
$params['query_name'] = $dfResults->getProperty('query_name');
|
229 |
+
$params['filter'] = $filter;
|
230 |
+
}
|
231 |
+
$dfResults = new Results($this->apiCall('search', $params));
|
232 |
+
$this->page = $dfResults->getProperty('page');
|
233 |
+
$this->total = $dfResults->getProperty('total');
|
234 |
+
$this->search_options['query'] = $dfResults->getProperty('query');
|
235 |
+
$this->maxScore = $dfResults->getProperty('max_score');
|
236 |
+
$this->queryName = $dfResults->getProperty('query_name');
|
237 |
+
$this->lastQuery = $dfResults->getProperty('query');
|
238 |
+
|
239 |
+
return $dfResults;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* hasNext
|
244 |
+
*
|
245 |
+
* @return boolean true if there is another page of results
|
246 |
+
*/
|
247 |
+
public function hasNext(){
|
248 |
+
return $this->page * $this->getRpp() < $this->total;
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
* hasPrev
|
253 |
+
*
|
254 |
+
* @return true if there is a previous page of results
|
255 |
+
*/
|
256 |
+
public function hasPrev(){
|
257 |
+
return ($this->page - 1) * $this->getRpp() > 0;
|
258 |
+
}
|
259 |
+
|
260 |
+
|
261 |
+
/**
|
262 |
+
* getPage
|
263 |
+
*
|
264 |
+
* obtain the current page number
|
265 |
+
* @return int the page number
|
266 |
+
*/
|
267 |
+
public function getPage(){
|
268 |
+
return $this->page;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* setFilter
|
273 |
+
*
|
274 |
+
* set a filter for the query
|
275 |
+
* @param string filterName the name of the filter to set
|
276 |
+
* @param array filter if simple array, terms filter assumed
|
277 |
+
* if 'from', 'to' in keys, range filter assumed
|
278 |
+
*/
|
279 |
+
public function setFilter($filterName, $filter){
|
280 |
+
$this->search_options['filter'][$filterName] = (array) $filter;
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* getFilter
|
285 |
+
*
|
286 |
+
* get conditions for certain filter
|
287 |
+
* @param string filterName
|
288 |
+
* @return array filter conditions: - simple array if terms filter
|
289 |
+
* - 'from', 'to' assoc array if range f.
|
290 |
+
* @return false if no filter definition found
|
291 |
+
*/
|
292 |
+
public function getFilter($filterName){
|
293 |
+
if (isset($this->search_options['filter'][$filterName])) {
|
294 |
+
return $this->search_options['filter'][$filterName];
|
295 |
+
}
|
296 |
+
|
297 |
+
return false;
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* getFilters
|
302 |
+
*
|
303 |
+
* get all filters and their configs
|
304 |
+
* @return array assoc array filterName => filterConditions
|
305 |
+
*/
|
306 |
+
public function getFilters() {
|
307 |
+
if (isset($this->search_options['filter'])) {
|
308 |
+
return $this->search_options['filter'];
|
309 |
+
}
|
310 |
+
|
311 |
+
return array();
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* addTerm
|
316 |
+
*
|
317 |
+
* add a term to a terms filter
|
318 |
+
* @param string filterName the filter to add the term to
|
319 |
+
* @param string term the term to add
|
320 |
+
*/
|
321 |
+
public function addTerm($filterName, $term) {
|
322 |
+
$this->search_options['filter'][$filterName][] = $term;
|
323 |
+
}
|
324 |
+
|
325 |
+
/**
|
326 |
+
* removeTerm
|
327 |
+
*
|
328 |
+
* remove a term from a terms filter
|
329 |
+
* @param string filterName the filter to remove the term from
|
330 |
+
* @param string term the term to be removed
|
331 |
+
*/
|
332 |
+
public function removeTerm($filterName, $term) {
|
333 |
+
if (isset($this->search_options['filter'][$filterName])) {
|
334 |
+
$idx = array_search($term, $this->search_options['filter'][$filterName]);
|
335 |
+
if ($idx !== false) {
|
336 |
+
array_splice($this->search_options['filter'][$filterName], $idx, 1);
|
337 |
+
}
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* setRange
|
343 |
+
*
|
344 |
+
* set a range filter
|
345 |
+
* @param string filterName the filter to set
|
346 |
+
* @param int from the lower bound value. included
|
347 |
+
* @param int to the upper bound value. included
|
348 |
+
*/
|
349 |
+
public function setRange($filterName, $from = null, $to = null) {
|
350 |
+
if (!is_null($from))
|
351 |
+
{
|
352 |
+
$this->search_options['filter'][$filterName]['from'] = $from;
|
353 |
+
}
|
354 |
+
if (!is_null($to))
|
355 |
+
{
|
356 |
+
$this->search_options['filter'][$filterName]['to'] = $to;
|
357 |
+
}
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* addSort
|
362 |
+
*
|
363 |
+
* Tells doofinder to sort results, or add a new field to a multiple fields sort
|
364 |
+
* @param string fieldName the field to sort by
|
365 |
+
* @param string direction 'asc' o 'desc'.
|
366 |
+
*/
|
367 |
+
public function addSort($sortName, $direction) {
|
368 |
+
$this->search_options['sort'][] = array($sortName => $direction);
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* toQuerystring
|
373 |
+
*
|
374 |
+
* 'serialize' the object's state to querystring params
|
375 |
+
* @param int $page the pagenumber. defaults to the current page
|
376 |
+
*/
|
377 |
+
public function toQuerystring($page = null){
|
378 |
+
$toParams = array();
|
379 |
+
|
380 |
+
foreach ($this->search_options as $paramName => $paramValue) {
|
381 |
+
if ($paramName == 'query') {
|
382 |
+
$toParams[$this->queryParameter] = $paramValue;
|
383 |
+
} else {
|
384 |
+
$toParams[$this->paramsPrefix.$paramName] = $paramValue;
|
385 |
+
}
|
386 |
+
}
|
387 |
+
|
388 |
+
if (!is_null($page)) {
|
389 |
+
$toParams[$this->paramsPrefix.'page'] = $page;
|
390 |
+
}
|
391 |
+
|
392 |
+
return http_build_query($toParams);
|
393 |
+
}
|
394 |
+
|
395 |
+
/**
|
396 |
+
* fromQuerystring
|
397 |
+
*
|
398 |
+
* obtain object's state from querystring params
|
399 |
+
* @param string $params where to obtain params from:
|
400 |
+
* - 'GET' $_GET params (default)
|
401 |
+
* - 'POST' $_POST params
|
402 |
+
*/
|
403 |
+
public function fromQuerystring(){
|
404 |
+
$filteredParams = array_filter(array_keys($this->serializationArray),
|
405 |
+
array($this, 'belongsToDoofinder'));
|
406 |
+
|
407 |
+
foreach ($filteredParams as $param) {
|
408 |
+
if ($param == $this->queryParameter) {
|
409 |
+
$key = 'query';
|
410 |
+
} else {
|
411 |
+
$key = substr($param, strlen($this->paramsPrefix));
|
412 |
+
}
|
413 |
+
|
414 |
+
$this->search_options[$key] = $this->serializationArray[$param];
|
415 |
+
}
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Ensures that range filters uses the most up to date syntax.
|
420 |
+
*
|
421 |
+
* array('from' => 9, 'to' => 20, 'other': 10)
|
422 |
+
*
|
423 |
+
* is converted into:
|
424 |
+
*
|
425 |
+
* array('gte' => 9, 'lte' => 20, 'other': 10)
|
426 |
+
*
|
427 |
+
* @param array $filter Filter definition.
|
428 |
+
* @return array Updated filter.
|
429 |
+
*/
|
430 |
+
private function updateFilter($filter) {
|
431 |
+
$new_filter = array();
|
432 |
+
|
433 |
+
foreach($filter as $key => $value) {
|
434 |
+
if ($key === 'from') {
|
435 |
+
$new_filter['gte'] = $value;
|
436 |
+
} else if ($key === 'to') {
|
437 |
+
$new_filter['lte'] = $value;
|
438 |
+
} else {
|
439 |
+
$new_filter[$key] = $value;
|
440 |
+
}
|
441 |
+
}
|
442 |
+
|
443 |
+
return $new_filter;
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* Cleans $params array by removing keys with empty/null values.
|
448 |
+
*
|
449 |
+
* @param array $params Array to be cleaned.
|
450 |
+
* @return array Array with no empty keys.
|
451 |
+
*/
|
452 |
+
private function sanitize($params) {
|
453 |
+
$result = array();
|
454 |
+
|
455 |
+
foreach ($params as $name => $value) {
|
456 |
+
if (is_array($value)) {
|
457 |
+
$result[$name] = $this->sanitize($value);
|
458 |
+
} else if (trim($value)) {
|
459 |
+
$result[$name] = $value;
|
460 |
+
}
|
461 |
+
}
|
462 |
+
|
463 |
+
return $result;
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* belongsToDoofinder
|
468 |
+
*
|
469 |
+
* to know if certain parameter name belongs to doofinder serialization parameters
|
470 |
+
*
|
471 |
+
* @param string $paramName name of the param
|
472 |
+
* @return boolean true or false.
|
473 |
+
*/
|
474 |
+
private function belongsToDoofinder($paramName){
|
475 |
+
if ($pos = strpos($paramName, '[')) {
|
476 |
+
$paramName = substr($paramName, 0, $pos);
|
477 |
+
}
|
478 |
+
|
479 |
+
return in_array($paramName, $this->allowedParameters) || $paramName == $this->queryParameter;
|
480 |
+
}
|
481 |
+
|
482 |
+
/**
|
483 |
+
* optionExists
|
484 |
+
*
|
485 |
+
* checks whether a search option is defined in $this->search_options
|
486 |
+
*
|
487 |
+
* @param string $optionName
|
488 |
+
* @return boolean
|
489 |
+
*/
|
490 |
+
private function optionExists($optionName) {
|
491 |
+
return array_key_exists($optionName, $this->search_options);
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* nextPage
|
496 |
+
*
|
497 |
+
* obtain the results for the next page
|
498 |
+
* @return DoofinderResults if there are results.
|
499 |
+
* @return null otherwise
|
500 |
+
*/
|
501 |
+
public function nextPage() {
|
502 |
+
return $this->hasNext() ? $this->query($this->lastQuery, $this->page + 1) : null;
|
503 |
+
}
|
504 |
+
|
505 |
+
|
506 |
+
/**
|
507 |
+
* prevPage
|
508 |
+
*
|
509 |
+
* obtain results for the previous page
|
510 |
+
* @return DoofinderResults
|
511 |
+
* @return null otherwise
|
512 |
+
*/
|
513 |
+
public function prevPage() {
|
514 |
+
return $this->hasPrev() ? $this->query($this->lastQuery, $this->page - 1) : null;
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* numPages
|
519 |
+
*
|
520 |
+
* @return integer the number of pages
|
521 |
+
*/
|
522 |
+
public function numPages() {
|
523 |
+
return ceil($this->total / $this->getRpp());
|
524 |
+
}
|
525 |
+
|
526 |
+
public function getRpp() {
|
527 |
+
$rpp = $this->optionExists('rpp') ? $this->search_options['rpp'] : null;
|
528 |
+
return $rpp ? $rpp : self::DEFAULT_RPP;
|
529 |
+
}
|
530 |
+
|
531 |
+
/**
|
532 |
+
* setApiVersion
|
533 |
+
*
|
534 |
+
* sets the api version to use.
|
535 |
+
* @param string $apiVersion the api version , '1.0', '3.0', '4' or '5'
|
536 |
+
*/
|
537 |
+
public function setApiVersion($apiVersion) {
|
538 |
+
switch (true) {
|
539 |
+
case in_array($apiVersion, array('1.0', '3.0')):
|
540 |
+
$this->authenticationHeader = false;
|
541 |
+
break;
|
542 |
+
case intval($apiVersion) == 4:
|
543 |
+
$this->authenticationHeader = 'API Token';
|
544 |
+
break;
|
545 |
+
case intval($apiVersion) == 5:
|
546 |
+
$this->authenticationHeader = 'Authorization';
|
547 |
+
break;
|
548 |
+
default:
|
549 |
+
throw new Error('Wrong API Version');
|
550 |
+
}
|
551 |
+
|
552 |
+
$this->apiVersion = $apiVersion;
|
553 |
+
}
|
554 |
+
|
555 |
+
/**
|
556 |
+
* setPrefix
|
557 |
+
*
|
558 |
+
* sets the prefix that will be used for serialization to querystring params
|
559 |
+
* @param string $prefix the prefix
|
560 |
+
*/
|
561 |
+
public function setPrefix($prefix) {
|
562 |
+
$this->paramsPrefix = $prefix;
|
563 |
+
}
|
564 |
+
|
565 |
+
/**
|
566 |
+
* getFilterType
|
567 |
+
* obtain the filter type (i.e. 'terms' or 'numeric range' from its conditions)
|
568 |
+
* @param array filter conditions
|
569 |
+
* @return string 'terms' or 'numericrange' false otherwise
|
570 |
+
*/
|
571 |
+
private function getFilterType($filter) {
|
572 |
+
if (is_array($filter)) {
|
573 |
+
if (array_key_exists('from', $filter) || array_key_exists('to', $filter)) {
|
574 |
+
return 'numericrange';
|
575 |
+
} else {
|
576 |
+
return 'term';
|
577 |
+
}
|
578 |
+
}
|
579 |
+
return false;
|
580 |
+
}
|
581 |
+
}
|
lib/php-doofinder/src/Search/Error.php
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Search;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Doofinder\Api\Search\Error
|
7 |
+
*
|
8 |
+
* Represents an error returned to the user of the library.
|
9 |
+
*/
|
10 |
+
class Error extends \Exception {}
|
lib/php-doofinder/src/Search/Results.php
ADDED
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Doofinder\Api\Search;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* @author JoeZ99 <jzarate@gmail.com>
|
6 |
+
* @author carlosescri <carlosescri@gmail.com>
|
7 |
+
*
|
8 |
+
* Results
|
9 |
+
*
|
10 |
+
* Very thin wrapper of the results obtained from the doofinder server
|
11 |
+
* it holds two accessors:
|
12 |
+
*
|
13 |
+
* - getProperty : get single property of the search results (rpp, page, etc..)
|
14 |
+
* - getResults: get an array with the results
|
15 |
+
*/
|
16 |
+
class Results {
|
17 |
+
|
18 |
+
// doofinder status
|
19 |
+
const SUCCESS = 'success'; // everything ok
|
20 |
+
const NOTFOUND = 'notfound'; // no account with the provided hashid found
|
21 |
+
const EXHAUSTED = 'exhausted'; // the account has reached its query limit
|
22 |
+
|
23 |
+
private $properties = array();
|
24 |
+
private $results = array();
|
25 |
+
private $facets = array();
|
26 |
+
private $filter = array();
|
27 |
+
|
28 |
+
public $status = null;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Constructor
|
32 |
+
*
|
33 |
+
* @param string $jsonResponse JSON returned by the Doofinder search server
|
34 |
+
*/
|
35 |
+
public function __construct($jsonResponse) {
|
36 |
+
$response = json_decode($jsonResponse, true);
|
37 |
+
|
38 |
+
foreach ($response as $key => $value){
|
39 |
+
if (!is_array($value)) {
|
40 |
+
$this->properties[$key] = $value;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
// Status
|
45 |
+
if (isset($this->properties['doofinder_status'])) {
|
46 |
+
$this->status = $this->properties['doofinder_status'];
|
47 |
+
} else {
|
48 |
+
$this->status = self::SUCCESS;
|
49 |
+
}
|
50 |
+
|
51 |
+
// Results
|
52 |
+
if (isset($response['results'])) {
|
53 |
+
$this->results = $response['results'];
|
54 |
+
}
|
55 |
+
|
56 |
+
// Build a "friendly" filters array
|
57 |
+
if (isset($response['filter'])) {
|
58 |
+
foreach($response['filter'] as $filterType => $filters) {
|
59 |
+
foreach($filters as $filterName => $filterProperties) {
|
60 |
+
$this->filter[$filterName] = $filterProperties;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
// facets
|
66 |
+
if (isset($response['facets'])) {
|
67 |
+
$this->facets = $response['facets'];
|
68 |
+
}
|
69 |
+
|
70 |
+
// mark "selected" true or false according to filters presence
|
71 |
+
foreach ($this->facets as $name => $properties) {
|
72 |
+
switch (true) {
|
73 |
+
case isset($properties['terms']):
|
74 |
+
foreach($properties['terms']['buckets'] as $idx => $bucket) {
|
75 |
+
$this->facets[$name]['terms']['buckets'][$idx]['selected'] = isset($this->filter[$name]) &&
|
76 |
+
in_array($bucket['key'], $this->filter[$name]);
|
77 |
+
}
|
78 |
+
break;
|
79 |
+
case isset($properties['range']):
|
80 |
+
foreach(array_keys($properties['range']['buckets']) as $idx) {
|
81 |
+
$this->facets[$name]['range']['buckets'][$idx]['selected_from'] = isset($this->filter[$name]['gte']) ? $this->filter[$name]['gte'] : false;
|
82 |
+
$this->facets[$name]['range']['buckets'][$idx]['selected_to'] = isset($this->filter[$name]['lte']) ? $this->filter[$name]['lte'] : false;
|
83 |
+
}
|
84 |
+
break;
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* getProperty
|
91 |
+
*
|
92 |
+
* get single property from the results
|
93 |
+
* @param string $name: 'results_per_page', 'query', 'max_score', 'page', 'total', 'hashid'
|
94 |
+
* @return mixed the value of the property
|
95 |
+
*/
|
96 |
+
public function getProperty($name) {
|
97 |
+
return array_key_exists($name, $this->properties) ? $this->properties[$name] : null;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* getResults
|
102 |
+
*
|
103 |
+
* @return array search results. at the moment, only the 'cooked' version.
|
104 |
+
* Each result is of the form:
|
105 |
+
* array('header'=>...,
|
106 |
+
* 'body' => ..,
|
107 |
+
* 'price' => ..,
|
108 |
+
* 'href' => ...,
|
109 |
+
* 'image' => ...,
|
110 |
+
* 'type' => ...,
|
111 |
+
* 'id' => ..)
|
112 |
+
*/
|
113 |
+
public function getResults(){
|
114 |
+
return $this->results;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
*
|
119 |
+
* getFacetsNames
|
120 |
+
*
|
121 |
+
* @return array facets names.
|
122 |
+
*/
|
123 |
+
public function getFacetsNames(){
|
124 |
+
return array_keys($this->facets);
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* getFacet
|
129 |
+
*
|
130 |
+
* @param string name the facet name whose results are wanted
|
131 |
+
*
|
132 |
+
* @return array facet search data
|
133 |
+
* - for terms facets
|
134 |
+
* array(
|
135 |
+
* '_type'=> 'terms', // type of facet 'terms' or 'range'
|
136 |
+
* 'missing'=> 3, // # of elements with no value for this facet
|
137 |
+
* 'others'=> 2, // # of terms not present in the search response
|
138 |
+
* 'total'=> 6, // # number of possible terms for this facet
|
139 |
+
* 'terms'=> array(
|
140 |
+
* array('count'=>6, 'term'=>'Blue', 'selected'=>false), // in the response, there are 6 'blue' terms
|
141 |
+
* array('count'=>3, 'term': 'Red', 'selected'=>true), // if 'selected'=>true, that term has been selected as filter
|
142 |
+
* ...
|
143 |
+
* )
|
144 |
+
* )
|
145 |
+
* - for range facets
|
146 |
+
* array(
|
147 |
+
* '_type'=> 'range',
|
148 |
+
* 'ranges'=> array(
|
149 |
+
* array(
|
150 |
+
* 'count'=>6, // in the response, 6 elements within that range.
|
151 |
+
* 'from':0,
|
152 |
+
* 'min': 30
|
153 |
+
* 'max': 90,
|
154 |
+
* 'mean'=>33.2,
|
155 |
+
* 'total'=>432,
|
156 |
+
* 'total_count'=>6,
|
157 |
+
* 'selected_from'=> 34.3 // if present. this value has been used as filter. false otherwise
|
158 |
+
* 'selected_to'=> 99.3 // if present. this value has been used as filter. false otherwise
|
159 |
+
* ),
|
160 |
+
* ...
|
161 |
+
* )
|
162 |
+
* )
|
163 |
+
*
|
164 |
+
*
|
165 |
+
*/
|
166 |
+
public function getFacet($facetName){
|
167 |
+
return $this->facets[$facetName];
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* getFacets
|
172 |
+
*
|
173 |
+
* get the whole facets associative array:
|
174 |
+
* array('color'=>array(...), 'brand'=>array(...))
|
175 |
+
* each array is defined as in getFacet() docstring
|
176 |
+
*
|
177 |
+
* @return array facets assoc. array
|
178 |
+
*/
|
179 |
+
public function getFacets(){
|
180 |
+
return $this->facets;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* getAppliedFilters
|
185 |
+
*
|
186 |
+
* get the filters the query has defined
|
187 |
+
* array('categories' => array( // filter name . same as facet name
|
188 |
+
* 'Sillas de paseo', // if simple array, it's a terms facet
|
189 |
+
* 'Sacos sillas de paseo'
|
190 |
+
* ),
|
191 |
+
* 'color' => array(
|
192 |
+
* 'red',
|
193 |
+
* 'blue'
|
194 |
+
* ),
|
195 |
+
* 'price' => array(
|
196 |
+
* 'include_upper'=>true, // if 'from' , 'to' keys, it's a range facet
|
197 |
+
* 'from'=>35.19,
|
198 |
+
* 'to'=>9999
|
199 |
+
* )
|
200 |
+
* )
|
201 |
+
* MEANING OF THE EXAMPLE FILTER:
|
202 |
+
* "FROM the query results, filter only results that have ('Sillas de paseo' OR 'Sacos sillas de paseo') categories
|
203 |
+
* AND ('red' OR 'blue') color AND price is BETWEEN 34.3 and 99.3"
|
204 |
+
|
205 |
+
*/
|
206 |
+
public function getAppliedFilters(){
|
207 |
+
return $this->filter;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* isOk
|
212 |
+
*
|
213 |
+
* checks if all went well
|
214 |
+
* @return boolean true if the status is 'success'.
|
215 |
+
* false if the status is not.
|
216 |
+
*/
|
217 |
+
public function isOk() {
|
218 |
+
return $this->status == self::SUCCESS;
|
219 |
+
}
|
220 |
+
}
|
lib/php-doofinder/src/Test/Management/AggregatesIteratorTest.php
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\AggregatesIterator;
|
6 |
+
use Doofinder\Api\Management\SearchEngine;
|
7 |
+
|
8 |
+
|
9 |
+
class AggregatesIteratorTest extends \PHPUnit_Framework_TestCase
|
10 |
+
{
|
11 |
+
protected function setUp()
|
12 |
+
{
|
13 |
+
$this->client = $this->prophesize('\Doofinder\Api\Management\Client');
|
14 |
+
$this->searchEngine = new SearchEngine($this->client->reveal(), 'testHashid', 'test SE');
|
15 |
+
}
|
16 |
+
|
17 |
+
public function testIterateThroughAllAggregates()
|
18 |
+
{
|
19 |
+
$firstResponse = array(
|
20 |
+
"next"=> "someUrl", "count"=>4, "previous"=>null, "aggregates" => array(
|
21 |
+
array("date"=> "2006-10-20", "searches"=> 1, "requests"=>33),
|
22 |
+
array("date"=> "2006-10-21", "searches"=> 2, "requests"=>34)
|
23 |
+
)
|
24 |
+
);
|
25 |
+
$secondResponse = array(
|
26 |
+
"next"=> "someUrl", "count"=>4, "previous"=>null, "aggregates" => array(
|
27 |
+
array("date"=> "2006-10-21", "searches"=> 3, "requests"=>33),
|
28 |
+
array("date"=> "2006-10-22", "searches"=> 4, "requests"=>34)
|
29 |
+
)
|
30 |
+
);
|
31 |
+
|
32 |
+
$thirdResponse = array(
|
33 |
+
"next"=> "someUrl", "count"=>6, "previous"=>null, "aggregates" => array()
|
34 |
+
);
|
35 |
+
|
36 |
+
// after receiving second request, prepare for third request and send second response
|
37 |
+
$prepareForThirdRequest = function($args, $client) use ($secondResponse, $thirdResponse) {
|
38 |
+
$client->managementApiCall(
|
39 |
+
'GET', 'testHashid/stats',
|
40 |
+
array('from'=>'20161020', 'to'=>'20161023', 'page'=>2)
|
41 |
+
)
|
42 |
+
->shouldBeCalledTimes(1)
|
43 |
+
->willReturn($thirdResponse);
|
44 |
+
return array('response'=>$secondResponse);
|
45 |
+
};
|
46 |
+
|
47 |
+
// after receiving first request, prepare for third request and send second response
|
48 |
+
$prepareForSecondRequest = function($args, $client) use ($prepareForThirdRequest, $firstResponse) {
|
49 |
+
$client->managementApiCall(
|
50 |
+
'GET', 'testHashid/stats',
|
51 |
+
array('from'=>'20161020', 'to'=>'20161023', 'page'=>2)
|
52 |
+
)
|
53 |
+
->shouldBeCalledTimes(1)
|
54 |
+
->will($prepareForThirdRequest);
|
55 |
+
return array('response'=>$firstResponse);
|
56 |
+
};
|
57 |
+
|
58 |
+
// first request
|
59 |
+
$this->client->managementApiCall(
|
60 |
+
'GET', 'testHashid/stats', array('from'=>'20161020', 'to'=>'20161023')
|
61 |
+
)
|
62 |
+
->shouldBeCalledTimes(1)
|
63 |
+
->will($prepareForSecondRequest);
|
64 |
+
|
65 |
+
|
66 |
+
$aggregates = new AggregatesIterator(
|
67 |
+
$this->searchEngine, new \Datetime('2016-10-20'), new \Datetime('2016-10-23')
|
68 |
+
);
|
69 |
+
|
70 |
+
$counter = 0;
|
71 |
+
foreach($aggregates as $agg){
|
72 |
+
$counter++;
|
73 |
+
$this->assertEquals($counter, $agg['searches']);
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
}
|
lib/php-doofinder/src/Test/Management/ClientTest.php
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\Client;
|
6 |
+
|
7 |
+
class ClientTest extends \PHPUnit_Framework_TestCase
|
8 |
+
{
|
9 |
+
|
10 |
+
protected function setUp()
|
11 |
+
{
|
12 |
+
$this->client = $this->getMockBuilder('Doofinder\Api\Management\Client')
|
13 |
+
->setConstructorArgs(array('xx1-testApiKey'))
|
14 |
+
->setMethods(array('talkToServer'))
|
15 |
+
->getMock();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function testManagementApiCallUrlAndHeaders()
|
19 |
+
{
|
20 |
+
// right zone-url formation
|
21 |
+
// right auth headers
|
22 |
+
$this->client->expects($this->once())
|
23 |
+
->method('talkToServer')
|
24 |
+
->with(
|
25 |
+
$this->equalTo('GET'),
|
26 |
+
$this->equalTo('https://xx1-api.doofinder.com/v1/'),
|
27 |
+
$this->equalTo(array("Authorization: Token testApiKey",
|
28 |
+
'Content-Type: application/json',
|
29 |
+
'Expect:'))
|
30 |
+
)
|
31 |
+
->willReturn(array('statusCode'=>200, 'contentResponse'=>''));
|
32 |
+
|
33 |
+
$this->client->getApiRoot();
|
34 |
+
}
|
35 |
+
|
36 |
+
public function testManagementApiCallEntryPointAndParams()
|
37 |
+
{
|
38 |
+
$expectedUrl = 'https://xx1-api.doofinder.com/v1/testHashid/items/product?foo=bar&cat%5B0%5D=cat1&cat%5B1%5D=cat2';
|
39 |
+
// right method, url and url params
|
40 |
+
$this->client->expects($this->once())
|
41 |
+
->method('talkToServer')
|
42 |
+
->with(
|
43 |
+
$this->equalTo('GET'),
|
44 |
+
$this->equalTo($expectedUrl),
|
45 |
+
$this->equalTo(array("Authorization: Token testApiKey",
|
46 |
+
'Content-Type: application/json',
|
47 |
+
'Expect:'))
|
48 |
+
)
|
49 |
+
->willReturn(array('statusCode'=>200, 'contentResponse'=>''));
|
50 |
+
|
51 |
+
$this->client->managementApiCall(
|
52 |
+
'GET', 'testHashid/items/product', array('foo'=>'bar', 'cat'=>array('cat1', 'cat2'))
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
public function testManagementApiCallGETMethodAndNoData()
|
57 |
+
{
|
58 |
+
$expectedUrl = 'https://xx1-api.doofinder.com/v1/testHashid/tasks/';
|
59 |
+
|
60 |
+
// with no POST, no DATA
|
61 |
+
$this->client->expects($this->once())
|
62 |
+
->method('talkToServer')
|
63 |
+
->with(
|
64 |
+
$this->equalTo('GET'),
|
65 |
+
$this->equalTo($expectedUrl),
|
66 |
+
$this->equalTo(array("Authorization: Token testApiKey",
|
67 |
+
'Content-Type: application/json',
|
68 |
+
'Expect:')),
|
69 |
+
null
|
70 |
+
)
|
71 |
+
->willReturn(array('statusCode'=>200, 'contentResponse'=>''));
|
72 |
+
$this->client->managementApiCall('GET', 'testHashid/tasks/', null, array('custom'=>'data'));
|
73 |
+
}
|
74 |
+
|
75 |
+
public function testManagementApiCallPOSTMethodAndData(){
|
76 |
+
$expectedUrl = 'https://xx1-api.doofinder.com/v1/testHashid/tasks/';
|
77 |
+
// with DATA, POST
|
78 |
+
$this->client->expects($this->once())
|
79 |
+
->method('talkToServer')
|
80 |
+
->with(
|
81 |
+
$this->equalTo('POST'),
|
82 |
+
$this->equalTo($expectedUrl),
|
83 |
+
$this->equalTo(array("Authorization: Token testApiKey",
|
84 |
+
'Content-Type: application/json',
|
85 |
+
'Expect:')),
|
86 |
+
array('custom'=>'data')
|
87 |
+
)
|
88 |
+
->willReturn(array('statusCode'=>200, 'contentResponse'=>''));
|
89 |
+
|
90 |
+
$this->client->managementApiCall('POST', 'testHashid/tasks/', null, array('custom'=>'data'));
|
91 |
+
|
92 |
+
}
|
93 |
+
|
94 |
+
}
|
lib/php-doofinder/src/Test/Management/ScrollIteratorTest.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\ScrollIterator;
|
6 |
+
use Doofinder\Api\Management\SearchEngine;
|
7 |
+
|
8 |
+
|
9 |
+
class ScrollIteratorTest extends \PHPUnit_Framework_TestCase
|
10 |
+
{
|
11 |
+
protected function setUp()
|
12 |
+
{
|
13 |
+
$this->client = $this->prophesize('\Doofinder\Api\Management\Client');
|
14 |
+
$this->searchEngine = new SearchEngine($this->client->reveal(), 'testHashid', 'test SE');
|
15 |
+
}
|
16 |
+
|
17 |
+
public function testIterateThroughAllResults()
|
18 |
+
{
|
19 |
+
$firstResponse = array(
|
20 |
+
"next"=> "someUrl", "count"=>6, "scroll_id"=>"testScroll", "results" => array(
|
21 |
+
array("id"=> "id1", "title"=> "title1"),
|
22 |
+
array("id"=> "id2", "title"=> "title2"),
|
23 |
+
array("id"=> "id3", "title"=> "title3")
|
24 |
+
)
|
25 |
+
);
|
26 |
+
$secondResponse = array(
|
27 |
+
"next"=> "someUrl", "count"=>6, "scroll_id"=>"testScroll", "results" => array(
|
28 |
+
array("id"=> "id4", "title"=> "title4"),
|
29 |
+
array("id"=> "id5", "title"=> "title5"),
|
30 |
+
array("id"=> "id6", "title"=> "title6")
|
31 |
+
)
|
32 |
+
);
|
33 |
+
$thirdResponse = array(
|
34 |
+
"next"=> "someUrl", "count"=>6, "scroll_id"=>"testScroll", "results" => array()
|
35 |
+
);
|
36 |
+
|
37 |
+
// after receiving second request, prepare for third request and send second response
|
38 |
+
$reassignMethodSignature = function($args, $client) use ($secondResponse, $thirdResponse) {
|
39 |
+
$client->managementApiCall(
|
40 |
+
'GET', 'testHashid/items/newType', array('scroll_id' => 'testScroll')
|
41 |
+
)
|
42 |
+
->shouldBeCalledTimes(1)
|
43 |
+
->willReturn(array('response'=>$thirdResponse));
|
44 |
+
return array('response'=>$secondResponse);
|
45 |
+
};
|
46 |
+
|
47 |
+
// first request
|
48 |
+
$this->client->managementApiCall('GET', 'testHashid/items/newType', null)
|
49 |
+
->shouldBeCalledTimes(1)
|
50 |
+
->willReturn(array('response'=>$firstResponse));
|
51 |
+
|
52 |
+
// second request
|
53 |
+
$this->client->managementApiCall(
|
54 |
+
'GET', 'testHashid/items/newType', array('scroll_id'=>'testScroll'))
|
55 |
+
->shouldBeCalledTimes(1)
|
56 |
+
->will($reassignMethodSignature);
|
57 |
+
|
58 |
+
$items = new ScrollIterator($this->searchEngine, 'newType');
|
59 |
+
|
60 |
+
$counter = 0;
|
61 |
+
foreach($items as $item){
|
62 |
+
$counter++;
|
63 |
+
$this->assertEquals("id$counter",$item['id']);
|
64 |
+
}
|
65 |
+
|
66 |
+
}
|
67 |
+
}
|
lib/php-doofinder/src/Test/Management/SearchEngineTest.php
ADDED
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test\Management;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\SearchEngine;
|
6 |
+
|
7 |
+
|
8 |
+
class SearchEngineTest extends \PHPUnit_Framework_TestCase
|
9 |
+
{
|
10 |
+
|
11 |
+
protected function setUp()
|
12 |
+
{
|
13 |
+
$this->client = $this->prophesize('\Doofinder\Api\Management\Client');
|
14 |
+
$this->searchEngine = new SearchEngine($this->client->reveal(), 'testHashid', 'test SE');
|
15 |
+
}
|
16 |
+
|
17 |
+
public function testGetTypesApiCall()
|
18 |
+
{
|
19 |
+
// Set up the expectation for the managemetApiCall() method
|
20 |
+
// to be called only once and with the strings 'GET' and 'testHashid/types'
|
21 |
+
// as its parameters.
|
22 |
+
$this->client->managementApiCall('GET', 'testHashid/types')->shouldBeCalledTimes(2);
|
23 |
+
|
24 |
+
$this->searchEngine->getTypes();
|
25 |
+
$this->searchEngine->getDataTypes();
|
26 |
+
}
|
27 |
+
|
28 |
+
public function testAddTypesApiCall()
|
29 |
+
{
|
30 |
+
$this->client->managementApiCall(
|
31 |
+
'POST',
|
32 |
+
'testHashid/types',
|
33 |
+
null,
|
34 |
+
json_encode(array('name'=>'newType'))
|
35 |
+
)->shouldBeCalledTimes(1);
|
36 |
+
|
37 |
+
$this->searchEngine->addType('newType');
|
38 |
+
}
|
39 |
+
|
40 |
+
public function testDeleteTypeApiCall()
|
41 |
+
{
|
42 |
+
$this->client->managementApiCall(
|
43 |
+
'DELETE',
|
44 |
+
'testHashid/types/newType'
|
45 |
+
)->shouldBeCalledTimes(1);
|
46 |
+
|
47 |
+
$this->searchEngine->deleteType('newType');
|
48 |
+
}
|
49 |
+
|
50 |
+
public function testItemsReturnsScrollIterator()
|
51 |
+
{
|
52 |
+
$scrollIterator = $this->searchEngine->items('newType');
|
53 |
+
// returns a ScrollIterator
|
54 |
+
$this->assertInstanceOf('\Doofinder\Api\Management\ScrollIterator', $scrollIterator);
|
55 |
+
// for the right Datatype
|
56 |
+
$this->assertEquals('newType', $this->exposeAttribute($scrollIterator, 'datatype'));
|
57 |
+
}
|
58 |
+
|
59 |
+
public function testGetItemApiCall()
|
60 |
+
{
|
61 |
+
$this->client->managementApiCall(
|
62 |
+
'GET',
|
63 |
+
'testHashid/items/newType/itemId'
|
64 |
+
)->shouldBeCalledTimes(1);
|
65 |
+
|
66 |
+
$this->searchEngine->getItem('newType', 'itemId');
|
67 |
+
}
|
68 |
+
|
69 |
+
public function testAddItemApiCall()
|
70 |
+
{
|
71 |
+
$item = array(
|
72 |
+
"id"=> "id1",
|
73 |
+
"title"=> "my title",
|
74 |
+
"categories"=> array("cat1", "cat2")
|
75 |
+
);
|
76 |
+
$this->client->managementApiCall(
|
77 |
+
'POST',
|
78 |
+
'testHashid/items/newType',
|
79 |
+
null,
|
80 |
+
json_encode($item)
|
81 |
+
)->shouldBeCalledTimes(1)->willReturn(array('response'=>array('id'=>'id1')));
|
82 |
+
|
83 |
+
$this->assertEquals('id1', $this->searchEngine->addItem('newType', $item));
|
84 |
+
|
85 |
+
}
|
86 |
+
|
87 |
+
public function testAddItemsApiCall()
|
88 |
+
{
|
89 |
+
$items = array(
|
90 |
+
array(
|
91 |
+
"id"=> "id1",
|
92 |
+
"title"=> "my title",
|
93 |
+
"categories"=> array("cat1", "cat2")
|
94 |
+
),
|
95 |
+
array(
|
96 |
+
"id"=> "id2",
|
97 |
+
"title"=> "my title2",
|
98 |
+
"categories"=> array("cat1", "cat2")
|
99 |
+
)
|
100 |
+
);
|
101 |
+
$this->client->managementApiCall(
|
102 |
+
'POST',
|
103 |
+
'testHashid/items/newType',
|
104 |
+
null,
|
105 |
+
json_encode($items)
|
106 |
+
)->shouldBeCalledTimes(1)->willReturn(
|
107 |
+
array(
|
108 |
+
'response'=>array(
|
109 |
+
array('id'=>'id1'), array('id'=>'id2')
|
110 |
+
)
|
111 |
+
)
|
112 |
+
);
|
113 |
+
|
114 |
+
$result = $this->searchEngine->addItems('newType', $items);
|
115 |
+
// returns ids inserted
|
116 |
+
$this->assertEquals(array('id1', 'id2'), $result);
|
117 |
+
}
|
118 |
+
|
119 |
+
public function testUpdateItemApiCall()
|
120 |
+
{
|
121 |
+
$item = array("id"=>"id1", "title"=>"title1", "cats"=>array("cat1", "cat2"));
|
122 |
+
$this->client->managementApiCall(
|
123 |
+
'PUT',
|
124 |
+
'testHashid/items/newType/idx',
|
125 |
+
null,
|
126 |
+
json_encode($item)
|
127 |
+
)->shouldBeCalledTimes(1)->willReturn(
|
128 |
+
array('statusCode' => 200));
|
129 |
+
|
130 |
+
$this->assertTrue($this->searchEngine->updateItem('newType', 'idx', $item));
|
131 |
+
}
|
132 |
+
|
133 |
+
public function testUpdateItemsApiCall()
|
134 |
+
{
|
135 |
+
$items = array(
|
136 |
+
array('id'=>'id1', 't'=>'t1', 'cats'=>array('cat1', 'cat2')),
|
137 |
+
array('id'=>'id2', 't'=>'t2', 'cats'=>array('cat1', 'cat2'))
|
138 |
+
);
|
139 |
+
$this->client->managementApiCall(
|
140 |
+
'PUT', 'testHashid/items/newType', null, json_encode($items)
|
141 |
+
)->willReturn(array('statusCode'=>200));
|
142 |
+
|
143 |
+
$this->assertTrue($this->searchEngine->updateItems('newType', $items));
|
144 |
+
}
|
145 |
+
|
146 |
+
public function testDeleteItemApiCall()
|
147 |
+
{
|
148 |
+
$this->client->managementApiCall('DELETE', 'testHashid/items/newType/id1')
|
149 |
+
-> willReturn(array('statusCode'=>204));
|
150 |
+
$this->assertTrue($this->searchEngine->deleteItem('newType', 'id1'));
|
151 |
+
}
|
152 |
+
|
153 |
+
public function testDeleteItemsApiCall()
|
154 |
+
{
|
155 |
+
$this->client->managementApiCall(
|
156 |
+
'DELETE', 'testHashid/items/newType', null,
|
157 |
+
json_encode(array(array('id'=>'id1'), array('id'=>'id2'), array('id'=>'id3')))
|
158 |
+
)
|
159 |
+
-> willReturn(
|
160 |
+
array(
|
161 |
+
'response'=>array(
|
162 |
+
'error'=>array(),
|
163 |
+
'success'=>array('id1', 'id2', 'id3')
|
164 |
+
)
|
165 |
+
)
|
166 |
+
);
|
167 |
+
$this->assertEquals(
|
168 |
+
$this->searchEngine->deleteItems('newType', array('id1', 'id2', 'id3')),
|
169 |
+
array('error'=>array(), 'success'=>array('id1', 'id2', 'id3'))
|
170 |
+
);
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @param \DateTime $from_date
|
175 |
+
* @param \DateTime $to_date
|
176 |
+
*
|
177 |
+
* @dataProvider providerTestStatsReturnsAggregatesIteratorWithDifferentDates
|
178 |
+
*/
|
179 |
+
public function testStatsReturnsAggregatesIteratorWithDifferentDates($from_date, $to_date)
|
180 |
+
{
|
181 |
+
if(is_null($from_date)){
|
182 |
+
$aggregatesIterator = $this->searchEngine->stats();
|
183 |
+
} else {
|
184 |
+
$aggregatesIterator = $this->searchEngine->stats($from_date, $to_date);
|
185 |
+
}
|
186 |
+
// returns a aggregatesIterator
|
187 |
+
$this->assertInstanceOf(
|
188 |
+
'\Doofinder\Api\Management\AggregatesIterator', $aggregatesIterator
|
189 |
+
);
|
190 |
+
// with the right searchEngine
|
191 |
+
$this->assertEquals(
|
192 |
+
$this->searchEngine, $this->exposeAttribute($aggregatesIterator, 'searchEngine')
|
193 |
+
);
|
194 |
+
// and proper dates
|
195 |
+
if(is_null($from_date)){
|
196 |
+
$expected = array();
|
197 |
+
} else {
|
198 |
+
$expected = array('from'=>$from_date->format("Ymd"), 'to'=>$to_date->format("Ymd"));
|
199 |
+
}
|
200 |
+
$this->assertEquals(
|
201 |
+
$expected, $this->exposeAttribute($aggregatesIterator, 'searchParams')
|
202 |
+
);
|
203 |
+
}
|
204 |
+
|
205 |
+
|
206 |
+
|
207 |
+
public function providerTestStatsReturnsAggregatesIteratorWithDifferentDates()
|
208 |
+
{
|
209 |
+
return array(
|
210 |
+
array(null, null), //default
|
211 |
+
array(new \DateTime("20011-01-07"), new \DateTime("2011-02-07"))
|
212 |
+
);
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* @param string term
|
217 |
+
* @param \DateTime $from_date
|
218 |
+
* @param \DateTime $to_date
|
219 |
+
*
|
220 |
+
* @dataProvider providerTestTopTermsReturnsTopTermsIteratorWithDifferentDatesAndTerms
|
221 |
+
*/
|
222 |
+
public function testTopTermsReturnsTopTermsIteratorWithDifferentDatesAndTerms($term, $from_date, $to_date)
|
223 |
+
{
|
224 |
+
if(is_null($from_date)){
|
225 |
+
$iterator = $this->searchEngine->topTerms($term);
|
226 |
+
} else {
|
227 |
+
$iterator = $this->searchEngine->topTerms($term, $from_date, $to_date);
|
228 |
+
}
|
229 |
+
// returns a aggregatesIterator
|
230 |
+
$this->assertInstanceOf(
|
231 |
+
'\Doofinder\Api\Management\TopTermsIterator', $iterator
|
232 |
+
);
|
233 |
+
// with the right searchEngine
|
234 |
+
$this->assertEquals(
|
235 |
+
$this->searchEngine, $this->exposeAttribute($iterator, 'searchEngine')
|
236 |
+
);
|
237 |
+
// and proper dates
|
238 |
+
if(is_null($from_date)){
|
239 |
+
$expected = array();
|
240 |
+
} else {
|
241 |
+
$expected = array('from'=>$from_date->format("Ymd"), 'to'=>$to_date->format("Ymd"));
|
242 |
+
}
|
243 |
+
$this->assertEquals(
|
244 |
+
$expected, $this->exposeAttribute($iterator, 'searchParams')
|
245 |
+
);
|
246 |
+
// and proter term
|
247 |
+
$this->assertEquals(
|
248 |
+
$term, $this->exposeAttribute($iterator, 'term')
|
249 |
+
);
|
250 |
+
}
|
251 |
+
|
252 |
+
|
253 |
+
|
254 |
+
public function providerTestTopTermsReturnsTopTermsIteratorWithDifferentDatesAndTerms()
|
255 |
+
{
|
256 |
+
return array(
|
257 |
+
array('clicked', null, null), //default
|
258 |
+
array('clicked', new \DateTime("20011-01-07"), new \DateTime("2011-02-07")),
|
259 |
+
array('searches', null, null), //default
|
260 |
+
array('searches', new \DateTime("20011-01-07"), new \DateTime("2011-02-07")),
|
261 |
+
array('opportunities', null, null), //default
|
262 |
+
array('opportunities', new \DateTime("20011-01-07"), new \DateTime("2011-02-07"))
|
263 |
+
);
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* @expectedException \Doofinder\Api\Management\Errors\BadRequest
|
268 |
+
*/
|
269 |
+
public function testTopTermsReturnsBadRequestWithWrongTerm()
|
270 |
+
{
|
271 |
+
$this->searchEngine->topTerms('bad_term');
|
272 |
+
}
|
273 |
+
|
274 |
+
public function testProcessApiCall()
|
275 |
+
{
|
276 |
+
$this->client->managementApiCall('POST','testHashid/tasks/process')
|
277 |
+
->shouldBeCalledTimes(1)
|
278 |
+
->willReturn(
|
279 |
+
array(
|
280 |
+
'statusCode' => 201,
|
281 |
+
'response' => array(
|
282 |
+
'link' => '/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/tasks/task_id'
|
283 |
+
)
|
284 |
+
)
|
285 |
+
);
|
286 |
+
$this->assertEquals(
|
287 |
+
$this->searchEngine->process(), array('task_created'=>true, 'task_id'=>'task_id')
|
288 |
+
);
|
289 |
+
|
290 |
+
}
|
291 |
+
|
292 |
+
public function testProcessInfoApiCall()
|
293 |
+
{
|
294 |
+
$this->client->managementApiCall('GET','testHashid/tasks/process')
|
295 |
+
->shouldBeCalledTimes(1)
|
296 |
+
->willReturn(
|
297 |
+
array(
|
298 |
+
'response' => array(
|
299 |
+
'state' => 'RUNNING',
|
300 |
+
'message' => 'ok'
|
301 |
+
)
|
302 |
+
)
|
303 |
+
);
|
304 |
+
$this->assertEquals(
|
305 |
+
$this->searchEngine->processInfo(), array('state'=>'RUNNING', 'message' => 'ok')
|
306 |
+
);
|
307 |
+
}
|
308 |
+
|
309 |
+
public function testTaskInfoApiCall()
|
310 |
+
{
|
311 |
+
$this->client->managementApiCall('GET','testHashid/tasks/task-long-id')
|
312 |
+
->shouldBeCalledTimes(1)
|
313 |
+
->willReturn(
|
314 |
+
array(
|
315 |
+
'response' => array(
|
316 |
+
'state' => 'RUNNING',
|
317 |
+
'message' => 'ok'
|
318 |
+
)
|
319 |
+
)
|
320 |
+
);
|
321 |
+
$this->assertEquals(
|
322 |
+
$this->searchEngine->taskInfo('task-long-id'),
|
323 |
+
array('state'=>'RUNNING', 'message' => 'ok')
|
324 |
+
);
|
325 |
+
}
|
326 |
+
|
327 |
+
public function testLogsApiCall()
|
328 |
+
{
|
329 |
+
$this->client->managementApiCall('GET','testHashid/logs')
|
330 |
+
->shouldBeCalledTimes(1)
|
331 |
+
->willReturn(
|
332 |
+
array(
|
333 |
+
'response' => array('a', 'response')
|
334 |
+
)
|
335 |
+
);
|
336 |
+
$this->assertEquals($this->searchEngine->logs(), array('a', 'response'));
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Expose protected/private attribute of an object.
|
341 |
+
*
|
342 |
+
* @param object &$object Instantiated object that we will run method on.
|
343 |
+
* @param string $attributeName Attribute to expose
|
344 |
+
*
|
345 |
+
* @return mixed Attribute Value.
|
346 |
+
*/
|
347 |
+
public function exposeAttribute(&$object, $attributeName)
|
348 |
+
{
|
349 |
+
$reflection = new \ReflectionClass(get_class($object));
|
350 |
+
$attribute = $reflection->getProperty($attributeName);
|
351 |
+
$attribute->setAccessible(true);
|
352 |
+
return $attribute->getValue($object);
|
353 |
+
}
|
354 |
+
|
355 |
+
}
|
lib/php-doofinder/src/Test/Management/TopTermsIteratorTest.php
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test;
|
4 |
+
|
5 |
+
use Doofinder\Api\Management\TopTermsIterator;
|
6 |
+
use Doofinder\Api\Management\SearchEngine;
|
7 |
+
|
8 |
+
|
9 |
+
class TopTermsIteratorTest extends \PHPUnit_Framework_TestCase
|
10 |
+
{
|
11 |
+
protected function setUp()
|
12 |
+
{
|
13 |
+
$this->client = $this->prophesize('\Doofinder\Api\Management\Client');
|
14 |
+
$this->searchEngine = new SearchEngine($this->client->reveal(), 'testHashid', 'test SE');
|
15 |
+
}
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @expectedException \Doofinder\Api\Management\Errors\NotProcessedResponse
|
19 |
+
*/
|
20 |
+
public function testNotProcessedResponse()
|
21 |
+
{
|
22 |
+
$this->client->managementApiCall('GET', "testHashid/stats/top_fakeTerm", [])
|
23 |
+
->shouldBeCalledTimes(1)
|
24 |
+
->willReturn(array('statusCode'=>202));
|
25 |
+
|
26 |
+
$terms = new TopTermsIterator($this->searchEngine, 'fakeTerm');
|
27 |
+
foreach($terms as $t){
|
28 |
+
echo "boo";
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param string $termName
|
34 |
+
*
|
35 |
+
* @dataProvider providerTermNames
|
36 |
+
*/
|
37 |
+
public function testIterateThroughAllTopTerms($termName)
|
38 |
+
{
|
39 |
+
$firstResponse = array(
|
40 |
+
"next"=> "someUrl", "count"=>3, "previous"=>null,
|
41 |
+
"start"=> '2016-10-20', 'end'=> '2016-10-23',
|
42 |
+
$termName => array(
|
43 |
+
array("term"=> "t1", "count"=> 1),
|
44 |
+
array("term"=> "t2", "count"=> 1)
|
45 |
+
)
|
46 |
+
);
|
47 |
+
$secondResponse = array(
|
48 |
+
"next"=> "someUrl", "count"=>3, "previous"=>null,
|
49 |
+
"start"=> '2016-10-20', 'end'=> '2016-10-23',
|
50 |
+
$termName => array(
|
51 |
+
array("term"=> "t3", "count"=> 1)
|
52 |
+
)
|
53 |
+
);
|
54 |
+
|
55 |
+
// after receiving first request, prepare for second request and send first response
|
56 |
+
$prepareForSecondRequest = function($args, $client) use (
|
57 |
+
$termName, $secondResponse, $firstResponse
|
58 |
+
) {
|
59 |
+
$client->managementApiCall(
|
60 |
+
'GET', "testHashid/stats/top_$termName",
|
61 |
+
array('from'=>'20161020', 'to'=>'20161023', 'page'=>2)
|
62 |
+
)
|
63 |
+
->shouldBeCalledTimes(1)
|
64 |
+
->willReturn(array('response'=>$secondResponse, 'statusCode'=>200));
|
65 |
+
return array('response'=>$firstResponse, 'statusCode'=>200);
|
66 |
+
};
|
67 |
+
|
68 |
+
// first request
|
69 |
+
$this->client->managementApiCall(
|
70 |
+
'GET', "testHashid/stats/top_$termName", array('from'=>'20161020', 'to'=>'20161023')
|
71 |
+
)
|
72 |
+
->shouldBeCalledTimes(1)
|
73 |
+
->will($prepareForSecondRequest);
|
74 |
+
|
75 |
+
|
76 |
+
$terms = new TopTermsIterator(
|
77 |
+
$this->searchEngine, $termName, new \Datetime('2016-10-20'), new \Datetime('2016-10-23')
|
78 |
+
);
|
79 |
+
|
80 |
+
$counter = 0;
|
81 |
+
foreach($terms as $term){
|
82 |
+
$counter++;
|
83 |
+
$this->assertEquals("t$counter", $term['term']);
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
public function providerTermNames()
|
88 |
+
{
|
89 |
+
return array(
|
90 |
+
array('searches'),
|
91 |
+
array('clicked'),
|
92 |
+
array('opportunities')
|
93 |
+
);
|
94 |
+
}
|
95 |
+
|
96 |
+
}
|
lib/php-doofinder/src/Test/Search/ClientTest.php
ADDED
@@ -0,0 +1,381 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test;
|
4 |
+
|
5 |
+
use \phpmock\phpunit\PHPMock;
|
6 |
+
|
7 |
+
|
8 |
+
use Doofinder\Api\Search\Client;
|
9 |
+
|
10 |
+
class ClientTest extends \PHPUnit_Framework_TestCase
|
11 |
+
{
|
12 |
+
|
13 |
+
use PHPMock;
|
14 |
+
|
15 |
+
public function setUp()
|
16 |
+
{
|
17 |
+
$this->testHashid = 'ffffffffffffffffffffffffffffffff';
|
18 |
+
$this->searchUrl = "https://eu1-search.doofinder.com/5/search";
|
19 |
+
$this->optionsUrl = "https://eu1-search.doofinder.com/5/options";
|
20 |
+
$nameSpace = 'Doofinder\API\Search';
|
21 |
+
$this->curl_init = $this->getFunctionMock($nameSpace, "curl_init");
|
22 |
+
$this->curl_setopt = $this->getFunctionMock($nameSpace, "curl_setopt");
|
23 |
+
$this->curl_getinfo = $this->getFunctionMock($nameSpace, "curl_getinfo");
|
24 |
+
$this->curl_exec = $this->getFunctionMock($nameSpace, "curl_exec");
|
25 |
+
$this->curl_close = $this->getFunctionMock($nameSpace, 'curl_close');
|
26 |
+
$this->curl_getinfo->expects($this->any())->willReturn(200);
|
27 |
+
$this->curl_setopt->expects($this->any());
|
28 |
+
//
|
29 |
+
$this->client = new Client($this->testHashid, 'eu1-testApiToken');
|
30 |
+
}
|
31 |
+
|
32 |
+
public function testAuthHeadersAreSent()
|
33 |
+
{
|
34 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
35 |
+
$this->curl_setopt->expects($this->any())->willReturnCallback(
|
36 |
+
function($session, $option, $value){
|
37 |
+
if($option == CURLOPT_HTTPHEADER){
|
38 |
+
$this->assertEquals($value, array('Expect:', 'Authorization: testApiToken'));
|
39 |
+
}
|
40 |
+
}
|
41 |
+
);
|
42 |
+
|
43 |
+
|
44 |
+
$this->client->getOptions();
|
45 |
+
}
|
46 |
+
|
47 |
+
public function testGetOptions(){
|
48 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
49 |
+
$this->curl_init->expects($this->once())->with(
|
50 |
+
$this->optionsUrl.'/'.$this->testHashid.'?hashid='.$this->testHashid
|
51 |
+
)->willReturn(332);
|
52 |
+
|
53 |
+
$this->client->getOptions();
|
54 |
+
}
|
55 |
+
|
56 |
+
public function testBasicAndMatchAllQuery()
|
57 |
+
{
|
58 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
59 |
+
// basic query
|
60 |
+
$basicQueryUrl = $this->searchUrl.'?query=ab&hashid='.$this->testHashid;
|
61 |
+
// match_all query
|
62 |
+
$matchAllQueryUrl = $this->searchUrl.'?query_name=match_all&hashid='.$this->testHashid;
|
63 |
+
$this->curl_init->expects($this->exactly(2))
|
64 |
+
->withConsecutive(
|
65 |
+
array($basicQueryUrl), array($matchAllQueryUrl)
|
66 |
+
)
|
67 |
+
->willReturn(333);
|
68 |
+
|
69 |
+
// basic query
|
70 |
+
$this->client->query('ab');
|
71 |
+
// match query
|
72 |
+
$this->client->query();
|
73 |
+
}
|
74 |
+
|
75 |
+
public function testFilteredQuery()
|
76 |
+
{
|
77 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
78 |
+
// filtered query
|
79 |
+
$searchParams = array(
|
80 |
+
'filter'=>array(
|
81 |
+
'color'=>array('rojo', 'verde')
|
82 |
+
),
|
83 |
+
'query_name'=>'match_and'
|
84 |
+
);
|
85 |
+
$filteredQueryUrl = $this->searchUrl.'?query=ab&'.http_build_query($searchParams).
|
86 |
+
'&hashid='.$this->testHashid;
|
87 |
+
$this->curl_init->expects($this->once())->with($filteredQueryUrl)->willReturn(33);
|
88 |
+
$this->client->query('ab', null, $searchParams);
|
89 |
+
}
|
90 |
+
|
91 |
+
public function testRequeryWhenFilterAndNoQueryName()
|
92 |
+
{
|
93 |
+
$this->curl_exec->expects($this->any())
|
94 |
+
->willReturn(json_encode(array('query_name'=>'test_queryname')));
|
95 |
+
// filtered query
|
96 |
+
$searchParams = array(
|
97 |
+
'filter'=>array(
|
98 |
+
'color'=>array('rojo', 'verde')
|
99 |
+
)
|
100 |
+
);
|
101 |
+
$preQueryUrl = $this->searchUrl.'?query=ab&hashid='.$this->testHashid;
|
102 |
+
$filteredQueryUrl = $this->searchUrl.'?query=ab&query_name=test_queryname&'
|
103 |
+
.http_build_query($searchParams).'&hashid='.$this->testHashid;
|
104 |
+
$this->curl_init->expects($this->exactly(2))
|
105 |
+
->withConsecutive(array($preQueryUrl), array($filteredQueryUrl));
|
106 |
+
$this->client->query('ab', null, $searchParams);
|
107 |
+
}
|
108 |
+
|
109 |
+
public function testAnyOptionGoesToURL()
|
110 |
+
{
|
111 |
+
$this->curl_exec->expects($this->once())
|
112 |
+
->willReturn(json_encode(array('query_name'=>'test_queryname')));
|
113 |
+
$searchQueryUrl= $this->searchUrl.'?query=ab&kiko=pepito&hashid='.$this->testHashid;
|
114 |
+
$this->curl_init->expects($this->once())->with($searchQueryUrl);
|
115 |
+
$this->client->query('ab', null, array('kiko'=>'pepito'));
|
116 |
+
}
|
117 |
+
|
118 |
+
public function testPageParameter()
|
119 |
+
{
|
120 |
+
$this->curl_exec->expects($this->once())
|
121 |
+
->willReturn(json_encode(array('query_name'=>'tq')));
|
122 |
+
$pagedQueryUrl = $this->searchUrl.'?query=ab&page=2&hashid='.$this->testHashid;
|
123 |
+
$this->curl_init->expects($this->once())->with($pagedQueryUrl);
|
124 |
+
$this->client->query('ab', 2);
|
125 |
+
}
|
126 |
+
|
127 |
+
public function testSanitizeEmptyValues()
|
128 |
+
{
|
129 |
+
$this->curl_exec->expects($this->once())
|
130 |
+
->willReturn(json_encode(array()));
|
131 |
+
$dirty = array('color'=>array('red', 'brand'=>''));
|
132 |
+
$clean = array('color'=>array('red'));
|
133 |
+
$cleanFilters = array('filter'=>$clean);
|
134 |
+
$dirtyFilters = array('filter'=>$dirty);
|
135 |
+
$cleanSParams = array_merge($cleanFilters, array('query_name'=>'a'));
|
136 |
+
$dirtySParams = array_merge($dirtyFilters, array('query_name'=>'a'));
|
137 |
+
// no mention of 'brand' in url
|
138 |
+
$sanitizedQueryUrl = $this->searchUrl.'?query=ab&'.http_build_query($cleanSParams)
|
139 |
+
."&hashid={$this->testHashid}";
|
140 |
+
$this->curl_init->expects($this->once())->with($sanitizedQueryUrl);
|
141 |
+
$this->client->query('ab', null, $dirtySParams);
|
142 |
+
}
|
143 |
+
|
144 |
+
public function testUpdateRangeFilterKeys()
|
145 |
+
{
|
146 |
+
$this->curl_exec->expects($this->once())->willReturn(json_encode(array()));
|
147 |
+
$cleanParams = array('filter'=>array('price'=>array('gte'=>2, 'lte'=>3)), 'query_name'=>'a');
|
148 |
+
$dirtyParams = array('filter'=>array('price'=>array('from'=>2, 'to'=>3)), 'query_name'=>'a');
|
149 |
+
$queryUrl = $this->searchUrl.'?query=ab&'.http_build_query($cleanParams)
|
150 |
+
."&hashid={$this->testHashid}";
|
151 |
+
$this->curl_init->expects($this->once())->with($queryUrl);
|
152 |
+
$this->client->query('ab', null, $dirtyParams);
|
153 |
+
}
|
154 |
+
|
155 |
+
public function testHasNextAndPrev()
|
156 |
+
{
|
157 |
+
$counter = 0;
|
158 |
+
$this->curl_exec->expects($this->exactly(4))->willReturnCallback(function() use(&$counter) {
|
159 |
+
$counter++;
|
160 |
+
switch($counter){
|
161 |
+
case 1:
|
162 |
+
// first request (page 2). has next page
|
163 |
+
return json_encode(array('total'=>44, 'page'=>2));
|
164 |
+
case 2:
|
165 |
+
// second request. (last page) doesn't have next page
|
166 |
+
return json_encode(array('total'=>44, 'page'=>44));
|
167 |
+
case 3:
|
168 |
+
// third request. have prev (page 2)
|
169 |
+
return json_encode(array('total'=>44, 'page'=>2));
|
170 |
+
case 4:
|
171 |
+
// fourth request. does not have prev (first page)
|
172 |
+
return json_encode(array('total'=>44, 'page'=>1));
|
173 |
+
}
|
174 |
+
});
|
175 |
+
|
176 |
+
$this->client->query('ab');// page 2
|
177 |
+
$this->assertTrue($this->client->hasNext());
|
178 |
+
$this->client->query('ab');// last page
|
179 |
+
$this->assertFalse($this->client->hasNext());
|
180 |
+
$this->client->query('ab');// page 2
|
181 |
+
$this->assertTrue($this->client->hasPrev());
|
182 |
+
$this->client->query('ab');// first page
|
183 |
+
$this->assertFalse($this->client->hasPrev());
|
184 |
+
}
|
185 |
+
|
186 |
+
public function testSetFilter()
|
187 |
+
{
|
188 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
189 |
+
$searchParams = array(
|
190 |
+
'filter'=>array(
|
191 |
+
'color'=>array('rojo', 'verde')
|
192 |
+
)
|
193 |
+
);
|
194 |
+
$filteredQueryUrl = $this->searchUrl.'?'.http_build_query($searchParams).'&query=ab&query_name=test_queryname&hashid='.$this->testHashid;
|
195 |
+
$this->curl_init->expects($this->once())->with($filteredQueryUrl);
|
196 |
+
$this->client->setFilter('color', array('rojo', 'verde'));
|
197 |
+
$this->client->query('ab', null, array('query_name'=>'test_queryname'));
|
198 |
+
}
|
199 |
+
|
200 |
+
public function testGetFilter()
|
201 |
+
{
|
202 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
203 |
+
$searchParams = array(
|
204 |
+
'filter'=>array('color'=>array('rojo', 'verde')),
|
205 |
+
'query_name'=>'a'
|
206 |
+
);
|
207 |
+
$this->client->query('ab', null, $searchParams);
|
208 |
+
$this->assertEquals($searchParams['filter']['color'], $this->client->getFilter('color'));
|
209 |
+
}
|
210 |
+
|
211 |
+
public function testGetFilters()
|
212 |
+
{
|
213 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
214 |
+
$sParams = array('filter'=>array('color'=>array('red'), 'brand'=>array('nike')),
|
215 |
+
'query_name'=>'a');
|
216 |
+
$this->client->query('ab', null, $sParams);
|
217 |
+
$this->assertEquals(array('color'=>array('red'), 'brand'=>array('nike')),
|
218 |
+
$this->client->getFilters());
|
219 |
+
}
|
220 |
+
|
221 |
+
public function testAddSort()
|
222 |
+
{
|
223 |
+
$this->curl_exec->expects($this->any())->willReturn(json_encode(array()));
|
224 |
+
$sortedQueryUrl = $this->searchUrl.'?'
|
225 |
+
.http_build_query(array('sort'=>array(array('rice'=>'desc'))))
|
226 |
+
."&query=ab&hashid={$this->testHashid}";
|
227 |
+
$this->curl_init->expects($this->once())->with($sortedQueryUrl);
|
228 |
+
$this->client->addSort('rice', 'desc');
|
229 |
+
$this->client->query('ab');
|
230 |
+
}
|
231 |
+
|
232 |
+
public function testNextPage()
|
233 |
+
{
|
234 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
235 |
+
json_encode(array('total'=>44,'page'=>1, 'query'=>'ab'))
|
236 |
+
);
|
237 |
+
$searchParams = array(
|
238 |
+
'filter'=>array('color'=>array('red')),
|
239 |
+
'sort'=>array(array('price'=>'desc')),
|
240 |
+
'query_name'=>'a',
|
241 |
+
'hashid'=>$this->testHashid
|
242 |
+
);
|
243 |
+
//->query('ab') url
|
244 |
+
$page1QueryUrl = $this->searchUrl.'?query=ab&'.http_build_query($searchParams);
|
245 |
+
// ->nextPage() url
|
246 |
+
$page2QueryUrl = $this->searchUrl.'?query=ab&'.http_build_query($searchParams).'&page=2';
|
247 |
+
$this->curl_init->expects($this->exactly(2))
|
248 |
+
->withConsecutive(array($page1QueryUrl), array($page2QueryUrl));
|
249 |
+
$this->client->query('ab', null, $searchParams);
|
250 |
+
$this->client->nextPage();
|
251 |
+
}
|
252 |
+
|
253 |
+
public function testPrevPage()
|
254 |
+
{
|
255 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
256 |
+
json_encode(array('total'=>44,'page'=>2, 'query'=>'ab'))
|
257 |
+
);
|
258 |
+
$searchParams = array(
|
259 |
+
'filter'=>array('color'=>array('red')),
|
260 |
+
'sort'=>array(array('price'=>'desc')),
|
261 |
+
'query_name'=>'a',
|
262 |
+
'hashid'=>$this->testHashid
|
263 |
+
);
|
264 |
+
//->query('ab', 2) url
|
265 |
+
$page2QueryUrl = $this->searchUrl.'?query=ab&page=2&'.http_build_query($searchParams);
|
266 |
+
//->->prevPage() url
|
267 |
+
$page1QueryUrl = $this->searchUrl.'?query=ab&page=1&'.http_build_query($searchParams);
|
268 |
+
$this->curl_init->expects($this->exactly(2))
|
269 |
+
->withConsecutive(array($page2QueryUrl), array($page1QueryUrl));
|
270 |
+
$this->client->query('ab', 2, $searchParams);
|
271 |
+
$this->client->prevPage();
|
272 |
+
}
|
273 |
+
|
274 |
+
public function testToQueryString()
|
275 |
+
{
|
276 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
277 |
+
json_encode(
|
278 |
+
array('total'=>44, 'page'=>1, 'query' =>'ab', 'query_name'=>'baba')
|
279 |
+
)
|
280 |
+
);
|
281 |
+
$searchParams = array(
|
282 |
+
'query_name'=>'baba',
|
283 |
+
'filter'=>array('color'=>array('red')),
|
284 |
+
'sort'=>array(array('price'=>'desc'))
|
285 |
+
);
|
286 |
+
$serialization = 'dfParam_query=ab&dfParam_query_name=baba&dfParam_filter%5Bcolor%5D%5B0%5D=red&dfParam_sort%5B0%5D%5Bprice%5D=desc';
|
287 |
+
$this->client->query('ab', null, $searchParams); // do the query to populate state
|
288 |
+
$this->assertEquals($serialization, $this->client->toQueryString());
|
289 |
+
}
|
290 |
+
|
291 |
+
public function testToQueryStringWithCustomPrefix()
|
292 |
+
{
|
293 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
294 |
+
json_encode(
|
295 |
+
array('total'=>44, 'page'=>1, 'query' =>'ab', 'query_name'=>'baba')
|
296 |
+
)
|
297 |
+
);
|
298 |
+
$searchParams = array(
|
299 |
+
'query_name'=>'baba',
|
300 |
+
'filter'=>array('color'=>array('red')),
|
301 |
+
'sort'=>array(array('price'=>'desc'))
|
302 |
+
);
|
303 |
+
// create client with custom prefix
|
304 |
+
$customPrefixClient = new Client(
|
305 |
+
$this->testHashid, 'eu1-testApiToken', false, array('prefix' => 'customP')
|
306 |
+
);
|
307 |
+
$customPrefixserialization = 'customPquery=ab&customPquery_name=baba&customPfilter%5Bcolor%5D%5B0%5D=red&customPsort%5B0%5D%5Bprice%5D=desc';
|
308 |
+
$customPrefixClient->query('ab', null, $searchParams); // do the query to populate state
|
309 |
+
$this->assertEquals($customPrefixserialization, $customPrefixClient->toQueryString());
|
310 |
+
}
|
311 |
+
|
312 |
+
public function testFromQueryString()
|
313 |
+
{
|
314 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
315 |
+
json_encode(
|
316 |
+
array('total'=>44, 'page'=>1, 'query' =>'ab', 'query_name'=>'baba')
|
317 |
+
)
|
318 |
+
);
|
319 |
+
// to fool fromQueryString
|
320 |
+
$_REQUEST = array(
|
321 |
+
'dfParam_query'=>'ab',
|
322 |
+
'dfParam_query_name'=>'baba',
|
323 |
+
'dfParam_filter'=> array('color'=>array('red')),
|
324 |
+
'dfParam_sort'=>array(array('price'=>'desc'))
|
325 |
+
);
|
326 |
+
$queryUrl = $this->searchUrl.'?query=ab&query_name=baba&filter%5Bcolor%5D%5B0%5D=red&sort%5B0%5D%5Bprice%5D=desc&hashid='.$this->testHashid;
|
327 |
+
$this->curl_init->expects($this->once())->with($queryUrl);
|
328 |
+
// unserialize client
|
329 |
+
$client = new Client($this->testHashid, 'eu1-testApiToken');
|
330 |
+
$client->fromQueryString();
|
331 |
+
$client->query(); // do the query
|
332 |
+
}
|
333 |
+
|
334 |
+
public function testFromQueryStringWithCustomPrefix()
|
335 |
+
{
|
336 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
337 |
+
json_encode(
|
338 |
+
array('total'=>44, 'page'=>1, 'query' =>'ab', 'query_name'=>'baba')
|
339 |
+
)
|
340 |
+
);
|
341 |
+
// to fool fromQueryString
|
342 |
+
$_REQUEST = array(
|
343 |
+
'customPquery'=>'ab',
|
344 |
+
'customPquery_name'=>'baba',
|
345 |
+
'customPfilter'=> array('color'=>array('red')),
|
346 |
+
'customPsort'=>array(array('price'=>'desc'))
|
347 |
+
);
|
348 |
+
// query that should be done with params from $_REQUEST
|
349 |
+
$queryUrl = $this->searchUrl.'?query=ab&query_name=baba&filter%5Bcolor%5D%5B0%5D=red&sort%5B0%5D%5Bprice%5D=desc&hashid='.$this->testHashid;
|
350 |
+
$this->curl_init->expects($this->once())->with($queryUrl);
|
351 |
+
// have to create new one here so it can see $_REQUEST
|
352 |
+
$client = new Client($this->testHashid, 'eu1-testApiToken', false, array('prefix'=>'customP'));
|
353 |
+
$client->fromQueryString(); // unserialize client
|
354 |
+
$client->query(); // do the query
|
355 |
+
}
|
356 |
+
|
357 |
+
public function testConstructorFromQueryStringWithCustomPrefix(){
|
358 |
+
$this->curl_exec->expects($this->any())->willReturn(
|
359 |
+
json_encode(
|
360 |
+
array('total'=>44, 'page'=>1, 'query' =>'ab', 'query_name'=>'baba')
|
361 |
+
)
|
362 |
+
);
|
363 |
+
// to fool fromQueryString
|
364 |
+
$_REQUEST = array(
|
365 |
+
'customPquery'=>'ab',
|
366 |
+
'customPquery_name'=>'baba',
|
367 |
+
'customPfilter'=> array('color'=>array('red')),
|
368 |
+
'customPsort'=>array(array('price'=>'desc'))
|
369 |
+
);
|
370 |
+
$queryUrl = $this->searchUrl.'?query=ab&query_name=baba&filter%5Bcolor%5D%5B0%5D=red&sort%5B0%5D%5Bprice%5D=desc&hashid='.$this->testHashid;
|
371 |
+
$this->curl_init->expects($this->once())->with($queryUrl);
|
372 |
+
// unserialize client in constructor
|
373 |
+
$client = new Client($this->testHashid, 'eu1-testApiToken', true, array('prefix'=>'customP'));
|
374 |
+
|
375 |
+
$client->query(); // do the query
|
376 |
+
}
|
377 |
+
|
378 |
+
|
379 |
+
|
380 |
+
|
381 |
+
}
|
lib/php-doofinder/src/Test/Search/ResultsTests.php
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Doofinder\Api\Test;
|
4 |
+
|
5 |
+
use Doofinder\Api\Search\Results;
|
6 |
+
|
7 |
+
class ResultsTest extends \PHPUnit_Framework_TestCase
|
8 |
+
{
|
9 |
+
public function setUp()
|
10 |
+
{
|
11 |
+
$this->response = json_encode(
|
12 |
+
array(
|
13 |
+
"page" => 2, "total" => 44, "results_per_page" => 12, "query" => "ab",
|
14 |
+
"hashid" => 'testHashid', 'max_score' => 2.23, 'query_name' => 'match_and',
|
15 |
+
'results' => array(
|
16 |
+
array('id'=>'id1', 'title' => 't1', 'cats'=>array('cat1', 'cat2')),
|
17 |
+
array('id'=>'id2', 'title' => 't2', 'cats'=>array('cat1', 'caxnt2')),
|
18 |
+
array('id'=>'id3', 'title' => 't3', 'cats'=>array('cat1ab', 'cat2'))
|
19 |
+
),
|
20 |
+
'facets' => array(
|
21 |
+
'price' => array('type'=>'range'),
|
22 |
+
'color' => array(
|
23 |
+
'doc_count' => 6,
|
24 |
+
'missing' => array('doc_count' => 0),
|
25 |
+
'terms' => array(
|
26 |
+
'buckets' => array(
|
27 |
+
array('key'=>'red', 'doc_count'=>344), array('key'=>'blue', 'doc_count'=>1)
|
28 |
+
)
|
29 |
+
),
|
30 |
+
'total' => array('value'=>6)
|
31 |
+
)
|
32 |
+
),
|
33 |
+
'filter' => array(
|
34 |
+
'terms' => array('color'=>array('red'))
|
35 |
+
)
|
36 |
+
)
|
37 |
+
);
|
38 |
+
|
39 |
+
$this->results = new Results($this->response);
|
40 |
+
}
|
41 |
+
|
42 |
+
public function testGetProperty()
|
43 |
+
{
|
44 |
+
$this->assertEquals('match_and', $this->results->getProperty('query_name'));
|
45 |
+
$this->assertEquals(2.23, $this->results->getProperty('max_score'));
|
46 |
+
}
|
47 |
+
|
48 |
+
public function testGetResults()
|
49 |
+
{
|
50 |
+
$res = array(
|
51 |
+
array('id'=>'id1', 'title' => 't1', 'cats'=>array('cat1', 'cat2')),
|
52 |
+
array('id'=>'id2', 'title' => 't2', 'cats'=>array('cat1', 'caxnt2')),
|
53 |
+
array('id'=>'id3', 'title' => 't3', 'cats'=>array('cat1ab', 'cat2'))
|
54 |
+
);
|
55 |
+
$this->assertEquals($res, $this->results->getResults());
|
56 |
+
}
|
57 |
+
|
58 |
+
public function testGetFacetsNames()
|
59 |
+
{
|
60 |
+
$this->assertEquals(array('price', 'color'), $this->results->getFacetsNames());
|
61 |
+
}
|
62 |
+
|
63 |
+
public function testGetFacet()
|
64 |
+
{
|
65 |
+
$this->assertEquals(array('type'=>'range'), $this->results->getFacet('price'));
|
66 |
+
}
|
67 |
+
|
68 |
+
public function testGetFacetsAndMarkFacetAsSelected()
|
69 |
+
{
|
70 |
+
$facets = array(
|
71 |
+
'price' => array('type'=>'range'),
|
72 |
+
'color' => array(
|
73 |
+
'doc_count' => 6,
|
74 |
+
'missing' => array('doc_count' => 0),
|
75 |
+
'terms' => array(
|
76 |
+
'buckets' => array(
|
77 |
+
array('key'=>'red', 'doc_count'=>344, 'selected'=>true),
|
78 |
+
array('key'=>'blue', 'doc_count'=>1, 'selected'=>false)
|
79 |
+
)
|
80 |
+
),
|
81 |
+
'total' => array('value'=>6)
|
82 |
+
)
|
83 |
+
);
|
84 |
+
$this->assertEquals($facets, $this->results->getFacets());
|
85 |
+
}
|
86 |
+
|
87 |
+
public function testGetAppliedFilters()
|
88 |
+
{
|
89 |
+
$this->assertEquals(
|
90 |
+
array('color'=>array('red')), $this->results->getAppliedFilters()
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
}
|
package.xml
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Doofinder_Feed</name>
|
4 |
-
<version>1.
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL 3.0)</license>
|
7 |
<channel>community</channel>
|
@@ -48,11 +48,11 @@ Doofinder is fast and innovative. With no doubt, it is the best search engine fo
|
|
48 |
You can get more info and create your account visiting the Doofinder site:
|
49 |

|
50 |
http://www.doofinder.com</description>
|
51 |
-
<notes>
|
52 |
<authors><author><name>Carlos Escribano Rey</name><user>doofinder</user><email>carlos@doofinder.com</email></author></authors>
|
53 |
-
<date>2017-01-
|
54 |
-
<time>
|
55 |
-
<contents><target name="magecommunity"><dir name="Doofinder"><dir name="Feed"><dir name="Block"><dir name="Adminhtml"><dir name="Log"><file name="View.php" hash="
|
56 |
<compatible/>
|
57 |
-
<dependencies><required><php><min>5.
|
58 |
</package>
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Doofinder_Feed</name>
|
4 |
+
<version>1.8.0</version>
|
5 |
<stability>stable</stability>
|
6 |
<license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL 3.0)</license>
|
7 |
<channel>community</channel>
|
48 |
You can get more info and create your account visiting the Doofinder site:
|
49 |

|
50 |
http://www.doofinder.com</description>
|
51 |
+
<notes>Big update: Doofinder PHP client library.</notes>
|
52 |
<authors><author><name>Carlos Escribano Rey</name><user>doofinder</user><email>carlos@doofinder.com</email></author></authors>
|
53 |
+
<date>2017-01-26</date>
|
54 |
+
<time>12:50:52</time>
|
55 |
+
<contents><target name="magecommunity"><dir name="Doofinder"><dir name="Feed"><dir name="Block"><dir name="Adminhtml"><dir name="Log"><file name="View.php" hash="03c3b1af6a8b61400228f328a6320f2d"/></dir><dir name="Map"><file name="Additional.php" hash="29b7565675e33603cde91b9ddc24b13a"/></dir></dir><file name="Integration.php" hash="4993674d96110e803768b5485114bab1"/><dir name="Settings"><dir name="Buttons"><file name="Generate.php" hash="f3d9678ce31253a3f4ef78f7462e1182"/><file name="ViewLog.php" hash="e1a8fe3545be5e1cdf1ce84e465bfc13"/></dir><dir name="Panel"><file name="Cron.php" hash="e93e0471544eef8d6cc1ea5c2a8037dd"/><file name="Crondescription.php" hash="3e2370104c73841bab4b56684ab8156c"/><file name="Datetime.php" hash="a555254d8d190245b530e24337518a24"/><file name="Description.php" hash="ff021f308a3e2a4fa7e2b85097122216"/><file name="File.php" hash="9d3083bd42678b63f8e8ce6f38464a16"/><file name="Hashdescription.php" hash="707aa2f0eff7a1e170e6355787e09e84"/><file name="Layerdescription.php" hash="f4841d566f1fac8d38f26168c0f0ec9c"/><file name="Message.php" hash="7ca8fb843ce000bf5c6e06886158e745"/></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="5df562485701662ad20c3534ce056994"/><file name="Log.php" hash="687c0666e8564a9a742eca7209a4dd45"/><file name="Search.php" hash="adedec93e07dadb8edfcac8dd74fa07c"/><file name="Tax.php" hash="3924ee3d1866960e32f0bb736052fbc7"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Backend"><file name="Cron.php" hash="733057e70e3829acbceddca44566f04f"/></dir><dir name="Validation"><file name="Hashid.php" hash="5c84ddebe10442c48bc8bca05a9253d1"/></dir></dir></dir></dir><dir name="CatalogSearch"><dir name="Resource"><file name="Fulltext.php" hash="a3ece9fe463669dc6c16a232f7160aba"/></dir></dir><file name="Config.php" hash="608999f8e038964cce511c58385d1e81"/><file name="Cron.php" hash="197f39e1bd59fd69e5e71199f2296990"/><file name="Generator.php" hash="19fde20c0d7444b2a17e58f1088c20a6"/><file name="Log.php" hash="2b99a4837cb292291aed2a2d0a5af4c0"/><dir name="Map"><dir name="Product"><file name="Abstract.php" hash="ec7ab8b3476740e9a1f17ee7e41ea50f"/><file name="Associated.php" hash="15a063d949bdee13c428268dd463a87e"/><file name="Bundle.php" hash="cdc7c9d33fc12b2c89570cbe067371ae"/><file name="Configurable.php" hash="78bc90cdd7a7bd8f8b79d91aa879c3f7"/><file name="Downloadable.php" hash="b7cbe65768f5bf80468cfb06a514a1da"/><file name="Grouped.php" hash="659ca91fcb1807355b9de1d7062e52a6"/><file name="Simple.php" hash="efd6a7f9d722211bc2b5e2e149244c9c"/><file name="Virtual.php" hash="ccb06b3dbb80041c2b746d23be589592"/></dir></dir><dir name="Mysql4"><dir name="Cron"><file name="Collection.php" hash="85d5544835a6cdef9108016580cf5ab1"/></dir><file name="Cron.php" hash="f95b812e618dc87641eb53bc035e6723"/><dir name="Log"><file name="Collection.php" hash="e31683db099aeb80508d067625143361"/></dir><file name="Log.php" hash="9ee95cf8e53d1e00b1b06098d25b04f0"/></dir><dir name="Observers"><file name="Feed.php" hash="52156389f225401667fe90534290100e"/><file name="Logs.php" hash="395d5b9d67b0c4f5aeda9896b582a03f"/><file name="Schedule.php" hash="52277ae8b984e8b3fc1a38c9adda7980"/></dir><dir name="Resource"><dir name="Mysql4"><file name="Setup.php" hash="e3e85f054845581f6d867a7f17d6f0dd"/></dir></dir><dir name="System"><dir name="Config"><dir name="Backend"><dir name="Map"><file name="Additional.php" hash="ae75ae59c24a9a67c68a7ea1d616003c"/></dir><dir name="Total"><file name="Limit.php" hash="a23092ea72cbe81e2779b086ad055bf6"/></dir></dir><file name="Reset.php" hash="e077170e620a06f0f09d197849ee9140"/><dir name="Source"><dir name="Product"><file name="Attributes.php" hash="894e760cd054b167d7b1cff60c6bd2fa"/></dir></dir></dir></dir><file name="Tools.php" hash="a5b84002fb1ef9896f90b926f64d83c9"/></dir><dir name="Test"><dir name="Controller"><dir name="Index"><dir name="fixtures"><file name="testConfig.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/><file name="testFeed.yaml" hash="694cf25a35a9a301a8ae678866937909"/><file name="testIndex.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/></dir><dir name="providers"><file name="testConfig.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/><file name="testFeed.yaml" hash="1ea2f638be8fdcea22ef47767ed8d7db"/><file name="testIndex.yaml" hash="0a1f21a3417389e0c0a13392c79a7a89"/></dir></dir><file name="Index.php" hash="2771de706303653d039818bd0f6590ea"/></dir><dir name="Model"><dir name="Product"><dir name="expectations"><file name="testGenerator.yaml" hash="232dda1f4fd88b8ef081393f08044731"/></dir><dir name="fixtures"><file name="testGenerator.yaml" hash="df25e3ca67fd98ab1b933c4951c599ef"/></dir><dir name="providers"><file name="testGenerator.yaml" hash="84779d5dcd8d92abdecf0cd5ee65cfb0"/></dir></dir><file name="Product.php" hash="6c45ae2b36c6cc721ef634855ed6d596"/></dir></dir><dir name="controllers"><file name="DoofinderFeedFeedController.php" hash="fd84f56326d52c415b3463c954e94453"/><file name="DoofinderFeedLogController.php" hash="cf5fde00612dd0e2b6820445e52daedf"/><file name="FeedController.php" hash="df1e299fed29c601e37d6b88292a8b42"/><file name="IndexController.php" hash="0111c79320841b64cf63efe2f9d10efe"/></dir><dir name="etc"><file name="config.xml" hash="6294122049d5ec4727c6da0abf688fff"/><file name="system.xml" hash="4c69b26ff91eedb582883e83a26b843d"/></dir><dir name="sql"><dir name="doofinder_feed_setup"><file name="mysql4-install-1.5.4.php" hash="9dc5ed4e10febbe75ab1911259a1c9fe"/><file name="mysql4-install-1.5.7.php" hash="85baa03d9c4d76f6b744ba107c21f8da"/><file name="mysql4-upgrade-1.5.4-1.5.5.php" hash="df7158f6d6cdded9bdfc5cb72c1dc8e3"/><file name="mysql4-upgrade-1.5.5-1.5.6.php" hash="0f3ca5263356a0bc83d9352b463944dc"/><file name="mysql4-upgrade-1.5.6-1.5.7.php" hash="b0180770655f36d6723483aa3bd1541f"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Doofinder_Feed.xml" hash="9d3b6fbbbec12708461c33260715451c"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="doofinder.xml" hash="a7b9105a4e613086340b042845793d9f"/></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="doofinder.xml" hash="48a8636096950914917461260416c355"/></dir></dir></dir></dir></target><target name="mage"><dir name="js"><dir name="doofinder"><file name="admin.js" hash="ca050b0527ae101c75532fbca1c4a274"/></dir></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="doofinder"><file name="styles.css" hash="d6ec303c3199db3ab4dffa8d2491105e"/></dir></dir></dir></dir></target><target name="magelib"><dir name="php-doofinder"><file name="CHANGELOG.txt" hash="b4fc19b1cb37810d8faa6a1e784ab32b"/><file name="README.md" hash="f9eda30362e3356947f86a9248122c2f"/><file name="autoload.php" hash="1ab27f8a639e30c5a25a6100f1a3035d"/><file name="composer.json" hash="76f54c3a763a3ae683fa677106b41027"/><file name="phpunit.xml" hash="97d89bb72790578c7ca60aebe7e3b1dd"/><dir name="src"><dir name="Management"><file name="AggregatesIterator.php" hash="431618213beb8294f6cab35376c935c8"/><file name="Client.php" hash="3d4dc1dbabcb42c7b1e4835ff5056d21"/><dir name="Errors"><file name="BadRequest.php" hash="45113fccee9500f4ee2a20e845ef8ee2"/><file name="NotAllowed.php" hash="98169302570fcf34a49277c5afbbed20"/><file name="NotFound.php" hash="9b08d95a549cfaea5562ecfa74a28746"/><file name="NotProcessedResponse.php" hash="63984cfbe90b6912e8d53faa293e65d6"/><file name="QuotaExhausted.php" hash="75f95704e509d7b5a4c61ebdb76208f5"/><file name="ThrottledResponse.php" hash="d3cd9c6df62246b5c3e80de3e1ecfc9c"/><file name="Utils.php" hash="be1220fc529292e8cc723dc9f68ef81d"/><file name="WrongResponse.php" hash="3c0fd79d6c04ac2c8f93984912369333"/></dir><file name="ItemsResultSet.php" hash="67532bd88b478fb4e0ce9f05d2ffd94a"/><file name="ScrollIterator.php" hash="0d916403f85a791af8e4e2240ad9e1be"/><file name="SearchEngine.php" hash="417e44ce9f11be52d3a6679161014eb8"/><file name="TopTermsIterator.php" hash="9ef2a784ce1d2cae81e577ce8c13ed08"/></dir><dir name="Search"><file name="Client.php" hash="857ed6a6594776f3060d87c2c9b86aca"/><file name="Error.php" hash="38a4283a860ed443f43a8452cc931c99"/><file name="Results.php" hash="87417657ec8a4908d6fd35868b782d7e"/></dir><dir name="Test"><dir name="Management"><file name="AggregatesIteratorTest.php" hash="eb9abdc3de26ac6396efc007373906ca"/><file name="ClientTest.php" hash="9958013403856eab97a44a14b8c32569"/><file name="ScrollIteratorTest.php" hash="34ef0111f1bb3633e6950151efd5e3ec"/><file name="SearchEngineTest.php" hash="a354a2e690851ab8d2b9a41495fcd979"/><file name="TopTermsIteratorTest.php" hash="9804ca4b89dce16d1e89b6614c11c2c0"/></dir><dir name="Search"><file name="ClientTest.php" hash="6aeac0a72fc4a4798233dfde15014543"/><file name="ResultsTests.php" hash="64503776af865c5d23e261b71d36a6bc"/></dir></dir></dir></dir></target></contents>
|
56 |
<compatible/>
|
57 |
+
<dependencies><required><php><min>5.4.0</min><max>6.0.0</max></php></required></dependencies>
|
58 |
</package>
|