├── .gitignore ├── app ├── code │ └── community │ │ └── Asm │ │ └── Solr │ │ ├── Resources │ │ ├── solr │ │ │ ├── magentocores │ │ │ │ └── conf │ │ │ │ │ ├── french │ │ │ │ │ ├── protwords.txt │ │ │ │ │ ├── synonyms.txt │ │ │ │ │ ├── stopwords.txt │ │ │ │ │ └── schema.xml │ │ │ │ │ ├── german │ │ │ │ │ ├── protwords.txt │ │ │ │ │ ├── synonyms.txt │ │ │ │ │ └── stopwords.txt │ │ │ │ │ ├── english │ │ │ │ │ ├── protwords.txt │ │ │ │ │ ├── synonyms.txt │ │ │ │ │ ├── stopwords.txt │ │ │ │ │ └── schema.xml │ │ │ │ │ ├── elevate.xml │ │ │ │ │ └── currency.xml │ │ │ ├── zoo.cfg │ │ │ └── solr.xml │ │ └── tomcat │ │ │ ├── solr.xml │ │ │ ├── solr-tomcat │ │ │ ├── README.txt │ │ │ └── server.xml │ │ ├── Block │ │ ├── Form │ │ │ └── Mini.php │ │ ├── Filter.php │ │ ├── Result │ │ │ ├── File │ │ │ │ └── List.php │ │ │ ├── Page │ │ │ │ └── List.php │ │ │ ├── Page.php │ │ │ └── File.php │ │ ├── FiltersApplied.php │ │ ├── Adminhtml │ │ │ └── System │ │ │ │ └── Config │ │ │ │ ├── EmptyIndex.php │ │ │ │ ├── SyncSynonyms.php │ │ │ │ └── TestConnection.php │ │ ├── Filter │ │ │ ├── File.php │ │ │ └── Page.php │ │ ├── Abstract.php │ │ ├── Facets.php │ │ └── Result.php │ │ ├── Model │ │ ├── Resource │ │ │ ├── Helper │ │ │ │ └── Mysql4.php │ │ │ ├── Setup.php │ │ │ ├── Page │ │ │ │ └── Collection.php │ │ │ ├── Product │ │ │ │ └── Collection.php │ │ │ ├── Indexqueue │ │ │ │ ├── File.php │ │ │ │ └── File │ │ │ │ │ └── Collection.php │ │ │ ├── Indexer │ │ │ │ ├── Fieldprocessor │ │ │ │ │ ├── Default.php │ │ │ │ │ ├── Abstract.php │ │ │ │ │ ├── Factory.php │ │ │ │ │ └── Tokenizer.php │ │ │ │ ├── File.php │ │ │ │ └── Cms.php │ │ │ └── Search.php │ │ ├── Page.php │ │ ├── Observer │ │ │ ├── Product.php │ │ │ ├── Attribute.php │ │ │ └── Index │ │ │ │ └── Page.php │ │ ├── Product.php │ │ ├── GarbageCollector.php │ │ ├── Cms │ │ │ └── Page.php │ │ ├── SynonymHandler.php │ │ ├── Indexer │ │ │ ├── Cms.php │ │ │ ├── File.php │ │ │ └── Catalog.php │ │ ├── Solr │ │ │ ├── Facet │ │ │ │ ├── FacetOption.php │ │ │ │ └── Facet.php │ │ │ ├── Query │ │ │ │ └── Modifier │ │ │ │ │ ├── Faceting.php │ │ │ │ │ └── Sorting.php │ │ │ └── Response.php │ │ └── Indexqueue │ │ │ └── File.php │ │ ├── controllers │ │ ├── IndexController.php │ │ ├── SuggestController.php │ │ ├── ResultController.php │ │ └── Adminhtml │ │ │ └── Solr │ │ │ └── System │ │ │ └── Config │ │ │ └── UtilityController.php │ │ ├── Tests │ │ └── Model │ │ │ └── Solr │ │ │ └── Asm_Solr_Model_Solr_QueryTest.php │ │ ├── etc │ │ ├── adminhtml.xml │ │ └── config.xml │ │ ├── Helper │ │ ├── Logger.php │ │ ├── ConnectionManager.php │ │ ├── Schema.php │ │ ├── ContentExtractor.php │ │ └── Data.php │ │ └── sql │ │ └── solr_setup │ │ └── mysql4-install-0.11.0.php ├── etc │ └── modules │ │ └── Asm_Solr.xml └── design │ ├── frontend │ └── base │ │ └── default │ │ ├── template │ │ └── solr │ │ │ ├── filter.phtml │ │ │ ├── breadcrumbs.phtml │ │ │ ├── filter_menu.phtml │ │ │ ├── form.mini.phtml │ │ │ ├── result.phtml │ │ │ ├── result │ │ │ ├── file.phtml │ │ │ ├── page.phtml │ │ │ ├── page │ │ │ │ └── list.phtml │ │ │ └── file │ │ │ │ └── list.phtml │ │ │ ├── filters_applied.phtml │ │ │ └── facets.phtml │ │ └── layout │ │ └── solr.xml │ └── adminhtml │ └── default │ └── default │ └── template │ └── solr │ └── system │ └── config │ ├── testconnection.phtml │ ├── syncsynonyms.phtml │ └── emptyindex.phtml ├── composer.json ├── modman ├── README.md └── lib └── Apache └── Solr ├── Compatibility ├── CompatibilityLayer.php ├── Solr4CompatibilityLayer.php └── Solr3CompatibilityLayer.php ├── Exception.php ├── ParserException.php ├── InvalidArgumentException.php ├── NoServiceAvailableException.php ├── HttpTransportException.php └── HttpTransport ├── Abstract.php └── Interface.php /.gitignore: -------------------------------------------------------------------------------- 1 | /app/code/community/Asm/Solr/NOTES.txt 2 | .idea -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/french/protwords.txt: -------------------------------------------------------------------------------- 1 | Magento -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/german/protwords.txt: -------------------------------------------------------------------------------- 1 | Magento -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/english/protwords.txt: -------------------------------------------------------------------------------- 1 | Magento -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Form/Mini.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | community 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Helper/Mysql4.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | 6 | 7 |
getTitle() ?>(getResultCount() ?>)
8 | 9 | getChildHtml('list_filter') ?> -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/breadcrumbs.phtml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asm/solr", 3 | "description": "Apache Solr for Magento", 4 | "authors": [ 5 | { 6 | "name": "Ingo Renner", 7 | "email": "ingo@infielddesign.com" 8 | } 9 | ], 10 | "minimum-stability": "stable", 11 | "require": { 12 | 13 | }, 14 | "require-dev": { 15 | "phpunit/phpunit": "3.7.*" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/filter_menu.phtml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 |
9 |
Show Results For:
10 | 11 | getChildHtml('product') ?> 12 | 13 | getChildHtml('page') ?> 14 | 15 | getChildHtml('file') ?> 16 |
-------------------------------------------------------------------------------- /modman: -------------------------------------------------------------------------------- 1 | app/etc/modules/Asm_Solr.xml app/etc/modules/Asm_Solr.xml 2 | app/code/community/Asm/Solr/ app/code/community/Asm/Solr/ 3 | lib/Apache/ lib/Apache/ 4 | app/design/frontend/base/default/layout/solr.xml app/design/frontend/base/default/layout/solr.xml 5 | app/design/frontend/base/default/template/solr/ app/design/frontend/base/default/template/solr/ 6 | app/design/adminhtml/default/default/template/solr/ app/design/adminhtml/default/default/template/solr/ 7 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Tests/Model/Solr/Asm_Solr_Model_Solr_QueryTest.php: -------------------------------------------------------------------------------- 1 | assertEmpty($query->getFilters()); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Page.php: -------------------------------------------------------------------------------- 1 | _init('cms/page'); 12 | 13 | // setting the resource collection to our own 14 | // otherwise it is the same as _init(). However, we're still calling _init() 15 | // in case the implementation higher up in the hierarchy changes 16 | $this->_setResourceModel('cms/page', 'solr/page_collection'); 17 | } 18 | } 19 | 20 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Observer/Product.php: -------------------------------------------------------------------------------- 1 | rebuildIndex(null, $event->getProduct()->getId()); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/french/synonyms.txt: -------------------------------------------------------------------------------- 1 | aaa => aaaa 2 | bbb => bbbb1 bbbb2 3 | ccc => cccc1,cccc2 4 | a\=>a => b\=>b 5 | a\,a => b\,b 6 | fooaaa,baraaa,bazaaa 7 | 8 | # Some synonym groups specific to this example 9 | GB,gib,gigabyte,gigabytes 10 | MB,mib,megabyte,megabytes 11 | Television, Televisions, TV, TVs 12 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 13 | #after us won't split it into two words. 14 | 15 | # Synonym mappings can be used for spelling correction too 16 | pixima => pixma 17 | 18 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/german/synonyms.txt: -------------------------------------------------------------------------------- 1 | aaa => aaaa 2 | bbb => bbbb1 bbbb2 3 | ccc => cccc1,cccc2 4 | a\=>a => b\=>b 5 | a\,a => b\,b 6 | fooaaa,baraaa,bazaaa 7 | 8 | # Some synonym groups specific to this example 9 | GB,gib,gigabyte,gigabytes 10 | MB,mib,megabyte,megabytes 11 | Television, Televisions, TV, TVs 12 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 13 | #after us won't split it into two words. 14 | 15 | # Synonym mappings can be used for spelling correction too 16 | pixima => pixma 17 | 18 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/english/synonyms.txt: -------------------------------------------------------------------------------- 1 | aaa => aaaa 2 | bbb => bbbb1 bbbb2 3 | ccc => cccc1,cccc2 4 | a\=>a => b\=>b 5 | a\,a => b\,b 6 | fooaaa,baraaa,bazaaa 7 | 8 | # Some synonym groups specific to this example 9 | GB,gib,gigabyte,gigabytes 10 | MB,mib,megabyte,megabytes 11 | Television, Televisions, TV, TVs 12 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 13 | #after us won't split it into two words. 14 | 15 | # Synonym mappings can be used for spelling correction too 16 | pixima => pixma 17 | 18 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Product.php: -------------------------------------------------------------------------------- 1 | _init('catalog/product'); 12 | 13 | // setting the resource collection to our own 14 | // otherwise it is the same as _init(). However, we're still calling _init() 15 | // in case the implementation higher up in the hierarchy changes 16 | $this->_setResourceModel('catalog/product', 'solr/product_collection'); 17 | } 18 | } 19 | 20 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/zoo.cfg: -------------------------------------------------------------------------------- 1 | # The number of milliseconds of each tick 2 | tickTime=2000 3 | # The number of ticks that the initial 4 | # synchronization phase can take 5 | initLimit=10 6 | # The number of ticks that can pass between 7 | # sending a request and getting an acknowledgement 8 | syncLimit=5 9 | 10 | # the directory where the snapshot is stored. 11 | # dataDir=/opt/zookeeper/data 12 | # NOTE: Solr defaults the dataDir to /zoo_data 13 | 14 | # the port at which the clients will connect 15 | # clientPort=2181 16 | # NOTE: Solr sets this based on zkRun / zkHost params 17 | 18 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Result/File/List.php: -------------------------------------------------------------------------------- 1 | getData('collection')) // if there isn't a page collection set... 14 | { 15 | $collection = Mage::getModel('solr/indexqueue_file')->getCollection(); // todo honestly this should not happen. 16 | 17 | $this->setData('collection', $collection); 18 | } 19 | 20 | return $this->getData('collection'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Result/Page/List.php: -------------------------------------------------------------------------------- 1 | getData('collection')) // if there isn't a page collection set... 16 | { 17 | $collection = Mage::getModel('solr/page')->getCollection(); // todo honestly this should not happen. 18 | 19 | $this->setData('collection', $collection); 20 | } 21 | 22 | return $this->getData('collection'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/form.mini.phtml: -------------------------------------------------------------------------------- 1 | helper('solr'); 5 | ?> 6 |
9 | 18 |
19 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/result.phtml: -------------------------------------------------------------------------------- 1 | getResultCount(); 6 | $_resultType = $this->getResultType(); 7 | ?> 8 | getMessagesBlock()->getGroupedHtml() ?> 9 | 10 |
11 |

getHeaderText() ?>

12 |
13 | 14 | getResultListHtml() ?> 15 | 16 |
17 |

getHeaderText() ?>

18 |
19 |

20 | getNoResultText() ?> 21 |

22 | 23 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/result/file.phtml: -------------------------------------------------------------------------------- 1 | getResultCount(); 6 | $_resultType = $this->getType(); 7 | ?> 8 | getMessagesBlock()->getGroupedHtml() ?> 9 | 10 |
11 |

getHeaderText() ?>

12 |
13 | 14 | getResultListHtml() ?> 15 | 16 |
17 |

getHeaderText() ?>

18 |
19 |

20 | getNoResultText() ?> 21 |

22 | 23 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/result/page.phtml: -------------------------------------------------------------------------------- 1 | getResultCount(); 6 | $_resultType = $this->getResultType(); 7 | ?> 8 | getMessagesBlock()->getGroupedHtml() ?> 9 | 10 |
11 |

getHeaderText() ?>

12 |
13 | 14 | getResultListHtml() ?> 15 | 16 |
17 |

getHeaderText() ?>

18 |
19 |

20 | getNoResultText() ?> 21 |

22 | 23 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Setup.php: -------------------------------------------------------------------------------- 1 | _isCollectionLoaded = true; 17 | 18 | return parent::_init($model, $entityModel); 19 | } 20 | 21 | /** 22 | * @param integer $size Collection size / total number of results found 23 | */ 24 | public function setSize($size) 25 | { 26 | $this->_totalRecords = intval($size); 27 | } 28 | 29 | } 30 | 31 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Product/Collection.php: -------------------------------------------------------------------------------- 1 | _isCollectionLoaded = true; 17 | 18 | return parent::_init($model, $entityModel); 19 | } 20 | 21 | /** 22 | * @param integer $size Collection size / total number of results found 23 | */ 24 | public function setSize($size) 25 | { 26 | $this->_totalRecords = intval($size); 27 | } 28 | 29 | } 30 | 31 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/etc/adminhtml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Solr Search 13 | 14 | 15 | Indexing 16 | 17 | 18 | Search Queries 19 | 20 | 21 | Search Logging 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/filters_applied.phtml: -------------------------------------------------------------------------------- 1 | 2 | getAppliedFilters() ?> 3 | 4 | 5 |
6 |

__('Currently Filtering by:') ?>

7 |
    8 | 9 |
  1. 10 | 11 | __($filter->getFacet()->getName()) ?>: 12 | 13 | stripTags($filter->getLabel()) ?> 14 | 15 | 18 | __('Remove This Item') ?> 19 | 20 |
  2. 21 | 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/FiltersApplied.php: -------------------------------------------------------------------------------- 1 | getResponse()->getFacetFields(); 17 | /** @var Asm_Solr_Model_Solr_Facet_Facet[] $facets */ 18 | $urlQuery = Mage::getModel('core/url')->getRequest()->getQuery(); 19 | 20 | foreach ($facets as $facet) { 21 | $attributeCode = $facet->getAttributeCode(); 22 | if (array_key_exists($attributeCode, $urlQuery)) { 23 | $optionValue = $urlQuery[$attributeCode]; 24 | $facetOption = $facet->getOption($optionValue); 25 | 26 | $filters[$attributeCode] = $facetOption; 27 | } 28 | } 29 | 30 | return $filters; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache Solr for Magento 2 | 3 | A Magento module to use Apache Solr for search. 4 | 5 | Currently supports indexing and searching of simple and configurable products. 6 | Next up on our roadmap is adding facets (layered navigation), consider this a 7 | working beta version. 8 | 9 | ## Features 10 | 11 | * Index and search products 12 | * Facetted Search (layered navigation) 13 | * Index CMS pages (search coming soon) 14 | * Synonym handling through "Catalog" -> "Search Terms" admin module 15 | * Detailed logging 16 | * Automated Solr install script 17 | 18 | ## Installation 19 | 20 | Use [modman](https://github.com/colinmollenhour/modman) to install the module: 21 | ``` 22 | modman clone git@github.com:infielddesign/magento-MagSolr.git 23 | ``` 24 | 25 | ##Credits 26 | 27 | Infield Design 28 | 29 | http://www.infielddesign.com 30 | 31 | * Ingo Renner ingo@infielddesign.com 32 | * Kevin Mitchell kevin@infielddesign.com 33 | * Ian Anderson ian@infielddesign.com 34 | 35 | [![Analytics](https://ga-beacon.appspot.com/UA-47888627-1/magento-MagSolr/readme)](https://github.com/infielddesign/magento-MagSolr) -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexqueue/File.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Asm_Solr_Model_Resource_Indexqueue_File extends Mage_Core_Model_Resource_Db_Abstract { 26 | 27 | protected function _construct() 28 | { 29 | $this->_init('solr/indexqueue_file', 'file_id'); 30 | } 31 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/GarbageCollector.php: -------------------------------------------------------------------------------- 1 | getProduct()->getEntityId(); 13 | $productDocumentId = Mage::helper('solr')->getProductDocumentId($productId); 14 | 15 | $this->deleteIndexDocument($productDocumentId); 16 | } 17 | 18 | /** 19 | * Listens for event indexqueue_file_delete_after 20 | * 21 | * @param Varien_Event_Observer $event 22 | */ 23 | public function deleteFile($event) 24 | { 25 | $fileId = $event->getIndexqueueFile()->getEntityId(); 26 | $fileDocumentId = Mage::helper('solr')->getFileDocumentId($fileId); 27 | 28 | $this->deleteIndexDocument($fileDocumentId); 29 | } 30 | 31 | protected function deleteIndexDocument($documentId) 32 | { 33 | $connection = Mage::helper('solr/connectionManager')->getConnection(); 34 | /** @var $connection Asm_Solr_Model_Solr_Connection */ 35 | 36 | $connection->deleteById($documentId); 37 | $connection->commit(); 38 | } 39 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/tomcat/solr-tomcat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # chkconfig: 345 99 1 3 | # description: Tomcat7 service 4 | # processname: java 5 | 6 | # Get LSB functions 7 | . /lib/lsb/init-functions 8 | 9 | export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64/ 10 | export TOMCAT_USER=tomcat 11 | export CATALINA_HOME=/opt/solr-tomcat/tomcat 12 | export CATALINA_PID=$CATALINA_HOME/bin/tomcat7.pid 13 | 14 | [ -d "$CATALINA_HOME" ] || { echo "Tomcat requires $CATALINA_HOME."; exit 1; } 15 | 16 | case $1 in 17 | 18 | start|stop|run) 19 | if su $TOMCAT_USER bash -c "$CATALINA_HOME/bin/catalina.sh $1"; then 20 | log_success_msg "Tomcat $1 successful" 21 | [ $1 == "stop" ] && rm -f $CATALINA_PID 22 | else 23 | log_failure_msg "Error in Tomcat $1: $?" 24 | fi 25 | ;; 26 | 27 | restart) 28 | $0 stop 29 | sleep 10 30 | $0 start 31 | ;; 32 | 33 | status) 34 | if [ -f "$CATALINA_PID" ]; then 35 | read kpid < "$CATALINA_PID" 36 | if ps --pid $kpid 2>&1 1>/dev/null; then 37 | echo "$0 is already running at ${kpid}" 38 | else 39 | echo "$CATALINA_PID found, but $kpid is not running" 40 | fi 41 | unset kpid 42 | else 43 | echo "$0 is stopped" 44 | fi 45 | ;; 46 | 47 | esac 48 | exit 0 -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/controllers/ResultController.php: -------------------------------------------------------------------------------- 1 | setKeywords($this->getRequest()->getParam('q')); 10 | 11 | $limit = 20; 12 | $offset = 0; 13 | $result->load($limit, $offset); 14 | 15 | $products = $result->getProductCollection(); 16 | 17 | foreach ($products as $product) 18 | { 19 | echo $product->getName() . '
'; 20 | } 21 | } 22 | 23 | 24 | public function productAction() 25 | { 26 | $this->loadLayout() 27 | ->renderLayout(); 28 | } 29 | 30 | public function pageAction() 31 | { 32 | $this 33 | ->loadLayout() 34 | ->renderLayout(); 35 | 36 | } 37 | 38 | public function fileAction() 39 | { 40 | $this 41 | ->loadLayout() 42 | ->renderLayout(); 43 | } 44 | 45 | public function indexAction() 46 | { 47 | 48 | $this->_redirect('*/*/product', array('_query' => $this->getRequest()->getParams())); 49 | } 50 | 51 | 52 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/tomcat/README.txt: -------------------------------------------------------------------------------- 1 | The Tomcat configuration files 2 | ================================= 3 | 4 | server.xml 5 | --------------------------------- 6 | This is the Tomcat server configuration file. It is almost identical to the one distributed when downloading Tomcat. 7 | The only difference is that when using this server.xml file Tomcat will only listen to requests coming from localhost. 8 | Also, the AJP Connector on port 8009 has been turned off since it is not needed for using Solr. 9 | 10 | solr.xml 11 | --------------------------------- 12 | This file is a so-called Tomcat Context fragment defining the $SOLR_HOME environment variable and where to find 13 | the Solr web application archive (solr.war). This file goes into $CATALINA_HOME/conf/Catalina/localhost/ 14 | where $CATALINA_HOME is the Tomcat installation directory. 15 | 16 | solr-tomcat 17 | --------------------------------- 18 | This is a simple start/stop script to keep Tomcat running as a service when rebooting the server. Make sure to adjust 19 | the variables at the top of the script as needed, especially JAVA_HOME and TOMCAT_USER might be different for your 20 | system. The script was written for a Ubuntu environment. Place this file into /etc/init.d 21 | Usage: 'service solr-tomcat start|stop|restart|status' -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/solr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexqueue/File/Collection.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class Asm_Solr_Model_Resource_Indexqueue_File_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract 26 | { 27 | 28 | /** 29 | * Init model for collection 30 | * 31 | */ 32 | protected function _construct() 33 | { 34 | $this->_init('solr/indexqueue_file'); 35 | } 36 | 37 | /** 38 | * @param integer $size Collection size / total number of results found 39 | */ 40 | public function setSize($size) 41 | { 42 | $this->_totalRecords = intval($size); 43 | } 44 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/Compatibility/CompatibilityLayer.php: -------------------------------------------------------------------------------- 1 | processEntityAction( 19 | $this, self::ENTITY, Mage_Index_Model_Event::TYPE_SAVE 20 | ); 21 | 22 | return $result; 23 | } 24 | 25 | protected function _beforeDelete() 26 | { 27 | Mage::getSingleton('index/indexer')->logEvent( 28 | $this, self::ENTITY, Mage_Index_Model_Event::TYPE_DELETE 29 | ); 30 | return parent::_beforeDelete(); 31 | } 32 | 33 | protected function _afterDelete() 34 | { 35 | parent::_afterDelete(); 36 | Mage::getSingleton('index/indexer')->indexEvents( 37 | self::ENTITY, Mage_Index_Model_Event::TYPE_DELETE 38 | ); 39 | } 40 | 41 | /** 42 | * Init indexing process after cms page delete commit 43 | * 44 | */ 45 | protected function _afterDeleteCommit() 46 | { 47 | parent::_afterDeleteCommit(); 48 | Mage::getSingleton('index/indexer')->indexEvents( 49 | self::ENTITY, Mage_Index_Model_Event::TYPE_DELETE 50 | ); 51 | } 52 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Helper/Logger.php: -------------------------------------------------------------------------------- 1 | log($message, Zend_Log::EMERG, $data); 25 | } 26 | 27 | public function alert($message, $data = null) 28 | { 29 | $this->log($message, Zend_Log::ALERT, $data); 30 | } 31 | 32 | public function critical($message, $data = null) 33 | { 34 | $this->log($message, Zend_Log::CRIT, $data); 35 | } 36 | 37 | public function error($message, $data = null) 38 | { 39 | $this->log($message, Zend_Log::ERR, $data); 40 | } 41 | 42 | public function warning($message, $data = null) 43 | { 44 | $this->log($message, Zend_Log::WARN, $data); 45 | } 46 | 47 | public function notice($message, $data = null) 48 | { 49 | $this->log($message, Zend_Log::NOTICE, $data); 50 | } 51 | 52 | public function info($message, $data = null) 53 | { 54 | $this->log($message, Zend_Log::INFO, $data); 55 | } 56 | 57 | public function debug($message, $data = null) 58 | { 59 | $this->log($message, Zend_Log::DEBUG, $data); 60 | } 61 | } 62 | 63 | 64 | ?> -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/solr/system/config/testconnection.phtml: -------------------------------------------------------------------------------- 1 | store = Mage::app()->getRequest()->getParam('store') ?: 0; 6 | 7 | ?> 8 | 35 | 39 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Adminhtml/System/Config/EmptyIndex.php: -------------------------------------------------------------------------------- 1 | getTemplate()) { 13 | $this->setTemplate('solr/system/config/emptyindex.phtml'); 14 | } 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * Unset some non-related element parameters 21 | * 22 | * @param Varien_Data_Form_Element_Abstract $element 23 | * @return string 24 | */ 25 | public function render(Varien_Data_Form_Element_Abstract $element) 26 | { 27 | $element->unsScope() 28 | ->unsCanUseWebsiteValue() 29 | ->unsCanUseDefaultValue(); 30 | 31 | return parent::render($element); 32 | } 33 | 34 | /** 35 | * Get the button and scripts contents 36 | * 37 | * @param Varien_Data_Form_Element_Abstract $element 38 | * @return string 39 | */ 40 | protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) 41 | { 42 | $originalData = $element->getOriginalData(); 43 | $this->addData(array( 44 | 'button_label' => Mage::helper('solr')->__($originalData['button_label']), 45 | 'html_id' => $element->getHtmlId(), 46 | 'ajax_url' => Mage::getSingleton('adminhtml/url')->getUrl('*/solr_system_config_utility/emptyindex') 47 | )); 48 | 49 | return $this->_toHtml(); 50 | } 51 | } 52 | 53 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Adminhtml/System/Config/SyncSynonyms.php: -------------------------------------------------------------------------------- 1 | getTemplate()) { 15 | $this->setTemplate('solr/system/config/syncsynonyms.phtml'); 16 | } 17 | 18 | return $this; 19 | } 20 | 21 | /** 22 | * Unset some non-related element parameters 23 | * 24 | * @param Varien_Data_Form_Element_Abstract $element 25 | * @return string 26 | */ 27 | public function render(Varien_Data_Form_Element_Abstract $element) 28 | { 29 | $element->unsScope() 30 | ->unsCanUseWebsiteValue() 31 | ->unsCanUseDefaultValue(); 32 | 33 | return parent::render($element); 34 | } 35 | 36 | /** 37 | * Get the button and scripts contents 38 | * 39 | * @param Varien_Data_Form_Element_Abstract $element 40 | * @return string 41 | */ 42 | protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) 43 | { 44 | $originalData = $element->getOriginalData(); 45 | $this->addData(array( 46 | 'button_label' => Mage::helper('solr')->__($originalData['button_label']), 47 | 'html_id' => $element->getHtmlId(), 48 | 'ajax_url' => Mage::getSingleton('adminhtml/url')->getUrl('*/solr_system_config_utility/syncsynonyms') 49 | )); 50 | 51 | return $this->_toHtml(); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Adminhtml/System/Config/TestConnection.php: -------------------------------------------------------------------------------- 1 | getTemplate()) { 13 | $this->setTemplate('solr/system/config/testconnection.phtml'); 14 | } 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * Unset some non-related element parameters 21 | * 22 | * @param Varien_Data_Form_Element_Abstract $element 23 | * @return string 24 | */ 25 | public function render(Varien_Data_Form_Element_Abstract $element) 26 | { 27 | $element->unsScope() 28 | ->unsCanUseWebsiteValue() 29 | ->unsCanUseDefaultValue(); 30 | 31 | return parent::render($element); 32 | } 33 | 34 | /** 35 | * Get the button and scripts contents 36 | * 37 | * @param Varien_Data_Form_Element_Abstract $element 38 | * @return string 39 | */ 40 | protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) 41 | { 42 | $originalData = $element->getOriginalData(); 43 | $this->addData(array( 44 | 'button_label' => Mage::helper('solr')->__($originalData['button_label']), 45 | 'html_id' => $element->getHtmlId(), 46 | 'ajax_url' => Mage::getSingleton('adminhtml/url')->getUrl('*/solr_system_config_utility/testconnection') 47 | )); 48 | 49 | return $this->_toHtml(); 50 | } 51 | } 52 | 53 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/elevate.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Observer/Attribute.php: -------------------------------------------------------------------------------- 1 | getEvent()->getForm(); 24 | $attribute = $event->getEvent()->getAttribute(); 25 | $fieldset = $form->getElement('front_fieldset'); 26 | 27 | $fieldset->addField('search_weight', 'select', array( 28 | 'name' => 'search_weight', 29 | 'label' => Mage::helper('catalog')->__('Search Weight'), 30 | 'values' => $this->getWeightOptions(), 31 | ), 'is_visible_in_advanced_search'); 32 | /** 33 | * Disable default search fields 34 | */ 35 | $attributeCode = $attribute->getAttributeCode(); 36 | 37 | if ($attributeCode == 'name') { 38 | $form->getElement('is_searchable')->setDisabled(1); 39 | } 40 | } 41 | 42 | /** 43 | * Gets an array of value/label pairs for the search weight drop down field 44 | * 45 | * @return array 46 | */ 47 | protected function getWeightOptions() { 48 | $options = array(); 49 | 50 | foreach ($this->weights as $value) { 51 | $options[] = array( 52 | 'value' => $value, 53 | 'label' => $value 54 | ); 55 | } 56 | 57 | return $options; 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/Fieldprocessor/Default.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | class Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Default extends Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Abstract { 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function getFieldName() { 29 | $multiValue = false; 30 | if (is_array($this->attributeValue)) { 31 | $multiValue = true; 32 | } 33 | 34 | return Mage::helper('solr/schema')->getFieldNameByAttribute($this->attribute, $multiValue); 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function getFieldValue() { 41 | $attributeValue = $this->attributeValue; 42 | 43 | if ($this->attribute->getBackendType() == 'datetime') { 44 | $attributeValue = Mage::helper('solr')->dateToIso($this->attributeValue); 45 | } 46 | 47 | return $attributeValue; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/solr/system/config/syncsynonyms.phtml: -------------------------------------------------------------------------------- 1 | section = Mage::app()->getRequest()->getParam('section') ?: 0; 6 | $ajaxRequestParameters->website = Mage::app()->getRequest()->getParam('website') ?: 0; 7 | $ajaxRequestParameters->store = Mage::app()->getRequest()->getParam('store') ?: 0; 8 | 9 | ?> 10 | 37 | 42 | -------------------------------------------------------------------------------- /app/design/adminhtml/default/default/template/solr/system/config/emptyindex.phtml: -------------------------------------------------------------------------------- 1 | section = Mage::app()->getRequest()->getParam('section') ?: 0; 6 | $ajaxRequestParameters->website = Mage::app()->getRequest()->getParam('website') ?: 0; 7 | $ajaxRequestParameters->store = Mage::app()->getRequest()->getParam('store') ?: 0; 8 | 9 | ?> 10 | 37 | 42 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Filter/File.php: -------------------------------------------------------------------------------- 1 | getUrl('*/*/'.$this->getType(), array('_query' => $this->getRequest()->getParams())); 16 | } 17 | 18 | public function getType() 19 | { 20 | return $this->_type; 21 | } 22 | 23 | public function getSolrType() 24 | { 25 | return $this->_solrType; 26 | } 27 | 28 | public function getTitle(){ 29 | 30 | $title = 'PDFs & Downloads'; 31 | 32 | return $title; 33 | } 34 | 35 | public function getResultCount() 36 | { 37 | return $this->getResult()->getCount(); 38 | } 39 | 40 | /** 41 | * @return Asm_Solr_Model_Result 42 | */ 43 | public function getResult() 44 | { 45 | // if we don't have a result set for us, let's make one 46 | if (!$this->getData('result')) // todo should we be running like 30 searches? 47 | { 48 | /** @var Asm_Solr_Model_Result $result */ 49 | $result = Mage::getModel('solr/result'); 50 | 51 | $query = $result->getQuery(); 52 | $query->setKeywords($this->getKeywords()); 53 | $query->addFilter('type', $this->getSolrType()); 54 | 55 | $result->load($this->getLimit(), $this->getOffset()); // todo is this even useful? 56 | 57 | $this->setData('result', $result); 58 | } 59 | 60 | return $this->getData('result'); 61 | } 62 | 63 | 64 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Filter/Page.php: -------------------------------------------------------------------------------- 1 | getUrl('*/*/'.$this->getType(), array('_query' => $this->getRequest()->getParams())); 16 | } 17 | 18 | public function getType() 19 | { 20 | return $this->_type; 21 | } 22 | 23 | public function getSolrType() 24 | { 25 | return $this->_solrType; 26 | } 27 | 28 | public function getTitle(){ 29 | 30 | $title = 'Website Pages'; 31 | 32 | return $title; 33 | } 34 | 35 | public function getResultCount() 36 | { 37 | return $this->getResult()->getCount(); 38 | } 39 | 40 | /** 41 | * @return Asm_Solr_Model_Result 42 | */ 43 | public function getResult() 44 | { 45 | // if we don't have a result set for us, let's make one 46 | if (!$this->getData('result')) // todo should we be running like 30 searches? 47 | { 48 | /** @var Asm_Solr_Model_Result $result */ 49 | $result = Mage::getModel('solr/result'); 50 | 51 | $query = $result->getQuery(); 52 | $query->setKeywords($this->getKeywords()); 53 | $query->addFilter('type', $this->getSolrType()); 54 | 55 | $result->load($this->getLimit(), $this->getOffset()); // todo is this even useful? 56 | 57 | $this->setData('result', $result); 58 | } 59 | 60 | return $this->getData('result'); 61 | } 62 | 63 | 64 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/french/stopwords.txt: -------------------------------------------------------------------------------- 1 | au 2 | aux 3 | avec 4 | ce 5 | ces 6 | dans 7 | de 8 | des 9 | du 10 | elle 11 | en 12 | et 13 | eux 14 | il 15 | je 16 | la 17 | le 18 | leur 19 | lui 20 | ma 21 | mais 22 | me 23 | même 24 | mes 25 | moi 26 | mon 27 | ne 28 | nos 29 | notre 30 | nous 31 | on 32 | ou 33 | par 34 | pas 35 | pour 36 | qu 37 | que 38 | qui 39 | sa 40 | se 41 | ses 42 | son 43 | sur 44 | ta 45 | te 46 | tes 47 | toi 48 | ton 49 | tu 50 | un 51 | une 52 | vos 53 | votre 54 | vous 55 | c 56 | d 57 | j 58 | l 59 | à 60 | m 61 | n 62 | s 63 | t 64 | y 65 | été 66 | étée 67 | étées 68 | étés 69 | étant 70 | suis 71 | es 72 | est 73 | sommes 74 | êtes 75 | sont 76 | serai 77 | seras 78 | sera 79 | serons 80 | serez 81 | seront 82 | serais 83 | serait 84 | serions 85 | seriez 86 | seraient 87 | étais 88 | était 89 | étions 90 | étiez 91 | étaient 92 | fus 93 | fut 94 | fûmes 95 | fûtes 96 | furent 97 | sois 98 | soit 99 | soyons 100 | soyez 101 | soient 102 | fusse 103 | fusses 104 | fût 105 | fussions 106 | fussiez 107 | fussent 108 | ayant 109 | eu 110 | eue 111 | eues 112 | eus 113 | ai 114 | as 115 | avons 116 | avez 117 | ont 118 | aurai 119 | auras 120 | aura 121 | aurons 122 | aurez 123 | auront 124 | aurais 125 | aurait 126 | aurions 127 | auriez 128 | auraient 129 | avais 130 | avait 131 | avions 132 | aviez 133 | avaient 134 | eut 135 | eûmes 136 | eûtes 137 | eurent 138 | aie 139 | aies 140 | ait 141 | ayons 142 | ayez 143 | aient 144 | eusse 145 | eusses 146 | eût 147 | eussions 148 | eussiez 149 | eussent 150 | ceci 151 | celà 152 | cet 153 | cette 154 | ici 155 | ils 156 | les 157 | leurs 158 | quel 159 | quels 160 | quelle 161 | quelles 162 | sans 163 | soi 164 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/SynonymHandler.php: -------------------------------------------------------------------------------- 1 | getConnection(); 32 | 33 | $query = $event->getDataObject(); 34 | /* @var $query Mage_CatalogSearch_Model_Query */ 35 | 36 | $baseWord = $query->getData('query_text'); 37 | $oldSynonyms = $query->getOrigData('synonym_for'); 38 | $newSynonyms = $query->getData('synonym_for'); 39 | 40 | $solrSynonyms = $connection->getSynonyms($baseWord); 41 | if (!empty($solrSynonyms) && $newSynonyms != $oldSynonyms) { 42 | // since there's no update/edit, simply remove previous mapping 43 | // otherwise synonyms only get added, but never removed to/from a base word 44 | $connection->deleteSynonym($baseWord); 45 | } 46 | 47 | $newSynonyms = Mage::helper('solr')->trimExplode(',', $newSynonyms); 48 | $connection->addSynonym($baseWord, $newSynonyms); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Indexer/Cms.php: -------------------------------------------------------------------------------- 1 | array( 8 | Mage_Index_Model_Event::TYPE_SAVE, 9 | Mage_Index_Model_Event::TYPE_MASS_ACTION, 10 | Mage_Index_Model_Event::TYPE_REINDEX, 11 | Mage_Index_Model_Event::TYPE_DELETE 12 | ), 13 | ); 14 | 15 | 16 | protected function _construct() 17 | { 18 | $this->_init('solr/indexer_cms'); 19 | } 20 | 21 | /** 22 | * Get Indexer name 23 | * 24 | * @return string 25 | */ 26 | public function getName() 27 | { 28 | return Mage::helper('solr')->__('Solr CMS Search Index'); 29 | } 30 | 31 | /** 32 | * Get Indexer description 33 | * 34 | * @return string 35 | */ 36 | public function getDescription() 37 | { 38 | return Mage::helper('solr')->__('Rebuild Solr CMS search index'); 39 | } 40 | 41 | /** 42 | * Register indexer required data inside event object 43 | * 44 | * @param Mage_Index_Model_Event $event 45 | */ 46 | protected function _registerEvent(Mage_Index_Model_Event $event) 47 | { 48 | if ($event->getEntity() == Asm_Solr_Model_Cms_Page::ENTITY 49 | && $event->getType() == Mage_Index_Model_Event::TYPE_SAVE 50 | ) { 51 | $event->setData('solr_update_page_id', $event->getDataObject()->getId()); 52 | } 53 | } 54 | 55 | /** 56 | * Process event based on event state data 57 | * 58 | * @param Mage_Index_Model_Event $event 59 | */ 60 | protected function _processEvent(Mage_Index_Model_Event $event) 61 | { 62 | if ($event->getData('solr_update_page_id')) { 63 | $this->callEventHandler($event); 64 | } 65 | } 66 | 67 | public function reindexAll() 68 | { 69 | $resource = $this->_getResource(); 70 | $resource->rebuildIndex(); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/Compatibility/Solr4CompatibilityLayer.php: -------------------------------------------------------------------------------- 1 | '; 22 | 23 | return $rawPost; 24 | } 25 | 26 | /** 27 | * Creates an optimize command XML string. 28 | * 29 | * @param boolean $waitFlush Is ignored. 30 | * @param boolean $waitSearcher 31 | * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception) 32 | * @return string An XML string 33 | */ 34 | public function createOptimizeXml($waitFlush = true, $waitSearcher = true) 35 | { 36 | $searcherValue = $waitSearcher ? 'true' : 'false'; 37 | 38 | $rawPost = ''; 39 | 40 | return $rawPost; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/sql/solr_setup/mysql4-install-0.11.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 8 | 9 | 10 | // create file index queue 11 | $table = $installer->getConnection() 12 | ->newTable($installer->getTable('solr/indexqueue_file')) 13 | ->addColumn('file_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array( 14 | 'identity' => true, 15 | 'unsigned' => true, 16 | 'nullable' => false, 17 | 'primary' => true, 18 | ), 'File ID') 19 | ->addColumn('cms_page_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array( 20 | 'unsigned' => true, 21 | 'nullable' => false, 22 | 'default' => '0', 23 | ), 'CMS page ID') 24 | ->addColumn('store_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array( 25 | 'unsigned' => true, 26 | 'nullable' => false, 27 | 'default' => '0', 28 | ), 'Store ID') 29 | ->addColumn('file_path', Varien_Db_Ddl_Table::TYPE_TEXT, null, array( 30 | 'nullable' => false, 31 | ), 'File path') 32 | ->addColumn('changed', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array( 33 | 'unsigned' => true, 34 | 'nullable' => false, 35 | 'default' => '0', 36 | ), 'File changed or added to tracking at') 37 | ->addColumn('indexed', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array( 38 | 'unsigned' => true, 39 | 'nullable' => false, 40 | 'default' => '0', 41 | ), 'File indexed at') 42 | ->setComment('Keeps track of which files have been indexed for which page'); 43 | $installer->getConnection()->createTable($table); 44 | 45 | 46 | // add search weight field to eav attributes 47 | $installer->getConnection()->addColumn( 48 | $installer->getTable('catalog/eav_attribute'), 49 | 'search_weight', 50 | array( 51 | 'type' => Varien_Db_Ddl_Table::TYPE_SMALLINT, 52 | 'unsigned' => true, 53 | 'nullable' => false, 54 | 'default' => '1', 55 | 'comment' => 'Search Weight', 56 | ) 57 | ); 58 | 59 | 60 | $installer->endSetup(); 61 | 62 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/english/stopwords.txt: -------------------------------------------------------------------------------- 1 | i 2 | me 3 | my 4 | myself 5 | we 6 | us 7 | our 8 | ours 9 | ourselves 10 | you 11 | your 12 | yours 13 | yourself 14 | yourselves 15 | he 16 | him 17 | his 18 | himself 19 | she 20 | her 21 | hers 22 | herself 23 | it 24 | its 25 | itself 26 | they 27 | them 28 | their 29 | theirs 30 | themselves 31 | what 32 | which 33 | who 34 | whom 35 | this 36 | that 37 | these 38 | those 39 | am 40 | is 41 | are 42 | was 43 | were 44 | be 45 | been 46 | being 47 | have 48 | has 49 | had 50 | having 51 | do 52 | does 53 | did 54 | doing 55 | would 56 | should 57 | could 58 | ought 59 | i'm 60 | you're 61 | he's 62 | she's 63 | it's 64 | we're 65 | they're 66 | i've 67 | you've 68 | we've 69 | they've 70 | i'd 71 | you'd 72 | he'd 73 | she'd 74 | we'd 75 | they'd 76 | i'll 77 | you'll 78 | he'll 79 | she'll 80 | we'll 81 | they'll 82 | isn't 83 | aren't 84 | wasn't 85 | weren't 86 | hasn't 87 | haven't 88 | hadn't 89 | doesn't 90 | don't 91 | didn't 92 | won't 93 | wouldn't 94 | shan't 95 | shouldn't 96 | can't 97 | cannot 98 | couldn't 99 | mustn't 100 | let's 101 | that's 102 | who's 103 | what's 104 | here's 105 | there's 106 | when's 107 | where's 108 | why's 109 | how's 110 | an 111 | the 112 | and 113 | but 114 | if 115 | or 116 | because 117 | as 118 | until 119 | while 120 | of 121 | at 122 | by 123 | for 124 | with 125 | about 126 | against 127 | between 128 | into 129 | through 130 | during 131 | before 132 | after 133 | above 134 | below 135 | to 136 | from 137 | up 138 | down 139 | in 140 | out 141 | on 142 | off 143 | over 144 | under 145 | again 146 | further 147 | then 148 | once 149 | here 150 | there 151 | when 152 | where 153 | why 154 | how 155 | all 156 | any 157 | both 158 | each 159 | few 160 | more 161 | most 162 | other 163 | some 164 | such 165 | no 166 | nor 167 | not 168 | only 169 | own 170 | same 171 | so 172 | than 173 | too 174 | very 175 | -------------------------------------------------------------------------------- /lib/Apache/Solr/Compatibility/Solr3CompatibilityLayer.php: -------------------------------------------------------------------------------- 1 | '; 22 | 23 | return $rawPost; 24 | } 25 | 26 | /** 27 | * Creates an optimize command XML string. 28 | * 29 | * @param boolean $waitFlush 30 | * @param boolean $waitSearcher 31 | * @param float $timeout Maximum expected duration of the commit operation on the server (otherwise, will throw a communication exception) 32 | * @return string An XML string 33 | */ 34 | public function createOptimizeXml($waitFlush = true, $waitSearcher = true) 35 | { 36 | $flushValue = $waitFlush ? 'true' : 'false'; 37 | $searcherValue = $waitSearcher ? 'true' : 'false'; 38 | 39 | $rawPost = ''; 40 | 41 | return $rawPost; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/Fieldprocessor/Abstract.php: -------------------------------------------------------------------------------- 1 | attribute = Mage::getSingleton('eav/config')->getAttribute( 48 | Mage_Catalog_Model_Product::ENTITY, 49 | $parameters['attributeCode'] 50 | ); 51 | 52 | $this->attributeValue = $parameters['attributeValue']; 53 | 54 | $this->fieldType = (string) Mage::getConfig()->getNode('global/index/fieldMap/' . $parameters['attributeCode'] . '/type'); 55 | } 56 | 57 | /** 58 | * Generates the Solr field name for a given Magento product field 59 | * 60 | * @return string Solr field name 61 | */ 62 | abstract public function getFieldName(); 63 | 64 | /** 65 | * Process the Solr field value from the Magento field's value 66 | * 67 | * @return string|array 68 | */ 69 | abstract public function getFieldValue(); 70 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/Fieldprocessor/Factory.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Factory { 27 | 28 | /** 29 | * Factory method for field processors 30 | * 31 | * @param string $attributeCode 32 | * @param mixed $value Attribute value 33 | * @return Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Abstract 34 | */ 35 | public function getFieldProcessor($attributeCode, $value = '') 36 | { 37 | $fieldProcessor = null; 38 | 39 | $fieldProcessorParameters = array( 40 | 'attributeCode' => $attributeCode, 41 | 'attributeValue' => $value 42 | ); 43 | 44 | $fieldMap = Mage::getConfig()->getNode('global/index/fieldMap')->asArray(); 45 | if (array_key_exists($attributeCode, $fieldMap)) { 46 | $processors = array_keys($fieldMap[$attributeCode]['processor']); 47 | $processorName = $processors[0]; 48 | 49 | $fieldProcessor = Mage::getResourceModel( 50 | 'solr/indexer_fieldprocessor_' . $processorName, 51 | $fieldProcessorParameters 52 | ); 53 | } else { 54 | $fieldProcessor = Mage::getResourceModel( 55 | 'solr/indexer_fieldprocessor_default', 56 | $fieldProcessorParameters 57 | ); 58 | } 59 | 60 | return $fieldProcessor; 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Abstract.php: -------------------------------------------------------------------------------- 1 | setData('store_id', $storeId); 20 | } 21 | 22 | /** 23 | * Retrieve store Id 24 | * 25 | * @return int 26 | */ 27 | public function getStoreId() 28 | { 29 | if (!$storeId = $this->getData('store_id')) { 30 | $storeId = Mage::app()->getStore()->getId(); 31 | } 32 | return $storeId; 33 | } 34 | 35 | public function getOffset() 36 | { 37 | if (!$this->getData('offset')) 38 | { 39 | $limit = $this->getRequest()->getParam('offset', 0); 40 | 41 | $this->setData('offset', $limit); 42 | } 43 | 44 | return $this->getData('offset'); 45 | } 46 | 47 | public function getLimit() 48 | { 49 | if (!$this->getData('limit')) 50 | { 51 | $limit = $this->getRequest()->getParam('limit', 25); 52 | 53 | $this->setData('limit', $limit); 54 | } 55 | 56 | return $this->getData('limit'); 57 | } 58 | 59 | public function getKeywords() 60 | { 61 | if (!$this->getData('keywords')) 62 | { 63 | $keywords = $this->getRequest()->getParam('q', ''); 64 | 65 | $this->setData('keywords', $keywords); 66 | } 67 | 68 | return $this->getData('keywords'); 69 | } 70 | 71 | public function getFilteredQuery() 72 | { 73 | if (!$this->getData('filtered_query')) 74 | { 75 | $filteredQuery = $this->getRequest()->getParam('fq', ''); 76 | 77 | $this->setData('filtered_query', $filteredQuery); 78 | } 79 | 80 | return $this->getData('filtered_query'); 81 | } 82 | 83 | public function getKeywordsCleaned() 84 | { 85 | return Asm_Solr_Model_Solr_Query::cleanKeywords($this->getKeywords()); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/Exception.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_Exception extends Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/ParserException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_ParserException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/Fieldprocessor/Tokenizer.php: -------------------------------------------------------------------------------- 1 | 28 | * 29 | * string 30 | * 31 | * 32 | * , 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * @author Ingo Renner 39 | */ 40 | class Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Tokenizer extends Asm_Solr_Model_Resource_Indexer_Fieldprocessor_Abstract { 41 | 42 | /** 43 | * @var string 44 | */ 45 | protected $delimiter = ','; 46 | 47 | 48 | /** 49 | * @inheritdoc 50 | */ 51 | public function __construct($parameters) { 52 | $delimiter = (string) Mage::getConfig()->getNode('global/index/fieldMap/' . $parameters['attributeCode'] . '/processor/tokenizer/delimiter'); 53 | if (!empty($delimiter)) { 54 | $this->delimiter = $delimiter; 55 | } 56 | 57 | parent::__construct($parameters); 58 | } 59 | 60 | /** 61 | * @inheritdoc 62 | */ 63 | public function getFieldName() { 64 | return $this->attribute->getAttributeCode() . '_' . $this->fieldType . 'M'; 65 | } 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | public function getFieldValue() { 71 | return explode($this->delimiter, $this->attributeValue); 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_InvalidArgumentException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/NoServiceAvailableException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_NoServiceAvailableException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Solr/Facet/FacetOption.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @method Asm_Solr_Model_Solr_Facet_Facet getFacet() 11 | * @method Asm_Solr_Model_Solr_Facet_FacetOption setFacet(Asm_Solr_Model_Solr_Facet_Facet $facet) 12 | * @method string getValue() 13 | * @method Asm_Solr_Model_Solr_Facet_FacetOption setLabel(string $label) 14 | * @method Asm_Solr_Model_Solr_Facet_FacetOption setValue(string $value) 15 | * @method Asm_Solr_Model_Solr_Facet_FacetOption setNumberOfResults(string $numberOfResults) 16 | */ 17 | class Asm_Solr_Model_Solr_Facet_FacetOption extends Varien_Object 18 | { 19 | 20 | /** 21 | * URL parameters to remove when adding/removing facet options 22 | * 23 | * @var array 24 | */ 25 | protected $unwantedUrlParameters; 26 | 27 | 28 | /** 29 | * Constructor, initializes URL parameters to remove when changing 30 | * facet combinations. 31 | * 32 | */ 33 | public function __construct() 34 | { 35 | // Go back to page 1 when adding/removing a facet option 36 | $this->unwantedUrlParameters = array( 37 | Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // reset page browser 38 | ); 39 | 40 | parent::__construct(); 41 | } 42 | 43 | /** 44 | * Gets the URL to add the facet option as a filter to the current query 45 | * 46 | * @return string URL with parameters to add a new filter to the query 47 | */ 48 | public function getAddUrl() 49 | { 50 | $query = array_merge( 51 | array($this->getFacet()->getName() => $this->getValue()), 52 | $this->unwantedUrlParameters 53 | ); 54 | 55 | return Mage::getUrl('*/*', array( 56 | '_current' => true, 57 | '_use_rewrite' => true, 58 | '_query' => $query 59 | )); 60 | } 61 | 62 | /** 63 | * Gets the current URL with the facet option's filter removed 64 | * 65 | * @return string URL with the facet option's filter parameter removed 66 | */ 67 | public function getRemoveUrl() 68 | { 69 | $query = array_merge( 70 | array($this->getFacet()->getName() => null), 71 | $this->unwantedUrlParameters 72 | ); 73 | 74 | return Mage::getUrl('*/*', array( 75 | '_current' => true, 76 | '_use_rewrite' => true, 77 | '_query' => $query 78 | )); 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Search.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | 12 | class Asm_Solr_Model_Resource_Search 13 | { 14 | 15 | /** 16 | * An instance of the Solr service 17 | * 18 | * @var Asm_Solr_Model_Solr_Connection 19 | */ 20 | protected $connection = NULL; 21 | 22 | /** 23 | * The search query 24 | * 25 | * @var Asm_Solr_Model_Solr_Query 26 | */ 27 | protected $query = NULL; 28 | 29 | /** 30 | * The search response 31 | * 32 | * @var Apache_Solr_Response 33 | */ 34 | protected $rawResponse = NULL; 35 | 36 | /** 37 | * Flag for marking a search 38 | * 39 | * @var boolean 40 | */ 41 | protected $hasSearched = FALSE; 42 | 43 | 44 | public function __construct(array $searchParameters = array()) 45 | { 46 | if (isset($searchParameters['connection'])) { 47 | $this->connection = $searchParameters['connection']; 48 | } else { 49 | $this->connection = Mage::helper('solr/connectionManager')->getConnection(); 50 | } 51 | } 52 | 53 | /** 54 | * Executes a query against a Solr server. 55 | * 56 | * 1) Gets the query string 57 | * 2) Conducts the actual search 58 | * 3) Checks debug settings 59 | * 60 | * @param Asm_Solr_Model_Solr_Query $query The query with keywords, filters, and so on. 61 | * @param integer $offset Result offset for pagination. 62 | * @param integer $limit Maximum number of results to return. If set to NULL, this value is taken from the query object. 63 | * @return Asm_Solr_Model_Solr_Response Solr response 64 | */ 65 | public function search(Asm_Solr_Model_Solr_Query $query, $offset = 0, $limit = 10) 66 | { 67 | Mage::dispatchEvent('asm_search_query_before', array('query' => $query)); 68 | $this->query = $query; 69 | 70 | if (empty($limit)) { 71 | $limit = $query->getResultsPerPage(); 72 | } 73 | 74 | try { 75 | $response = $this->connection->search( 76 | $query->getKeywords(), 77 | $offset, 78 | $limit, 79 | $query->getQueryParameters() 80 | ); 81 | } catch (RuntimeException $e) { 82 | $response = $this->connection->getResponse(); 83 | } 84 | 85 | $this->rawResponse = $response; 86 | $this->hasSearched = TRUE; 87 | 88 | return Mage::getModel('solr/solr_response', array('rawResponse' => $response)); 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Indexer/File.php: -------------------------------------------------------------------------------- 1 | array( 29 | Mage_Index_Model_Event::TYPE_SAVE, 30 | Mage_Index_Model_Event::TYPE_MASS_ACTION, 31 | Mage_Index_Model_Event::TYPE_REINDEX, 32 | Mage_Index_Model_Event::TYPE_DELETE 33 | ), 34 | ); 35 | 36 | protected function _construct() 37 | { 38 | $this->_init('solr/indexer_file'); 39 | } 40 | 41 | /** 42 | * Get Indexer name 43 | * 44 | * @return string 45 | */ 46 | public function getName() { 47 | return Mage::helper('solr')->__('Solr File Search Index'); 48 | } 49 | 50 | /** 51 | * Get Indexer description 52 | * 53 | * @return string 54 | */ 55 | public function getDescription() 56 | { 57 | return Mage::helper('solr')->__('Rebuild Solr CMS File search index'); 58 | } 59 | 60 | /** 61 | * Register indexer required data inside event object 62 | * 63 | * @param Mage_Index_Model_Event $event 64 | */ 65 | protected function _registerEvent(Mage_Index_Model_Event $event) { 66 | if ($event->getEntity() == Asm_Solr_Model_Indexqueue_File::ENTITY 67 | && $event->getType() == Mage_Index_Model_Event::TYPE_SAVE 68 | ) { 69 | $event->setData('solr_update_file_id', $event->getDataObject()->getId()); 70 | } 71 | } 72 | 73 | /** 74 | * Process event based on event state data 75 | * 76 | * @param Mage_Index_Model_Event $event 77 | */ 78 | protected function _processEvent(Mage_Index_Model_Event $event) { 79 | if ($event->getData('solr_update_file_id')) { 80 | $this->callEventHandler($event); 81 | } 82 | } 83 | 84 | public function reindexAll() 85 | { 86 | $resource = $this->_getResource(); 87 | $resource->rebuildIndex(); 88 | } 89 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/german/stopwords.txt: -------------------------------------------------------------------------------- 1 | aber 2 | alle 3 | allem 4 | allen 5 | aller 6 | alles 7 | als 8 | also 9 | am 10 | an 11 | ander 12 | andere 13 | anderem 14 | anderen 15 | anderer 16 | anderes 17 | anderm 18 | andern 19 | anderr 20 | anders 21 | auch 22 | auf 23 | aus 24 | bei 25 | bin 26 | bis 27 | bist 28 | da 29 | damit 30 | dann 31 | der 32 | den 33 | des 34 | dem 35 | die 36 | das 37 | daß 38 | derselbe 39 | derselben 40 | denselben 41 | desselben 42 | demselben 43 | dieselbe 44 | dieselben 45 | dasselbe 46 | dazu 47 | dein 48 | deine 49 | deinem 50 | deinen 51 | deiner 52 | deines 53 | denn 54 | derer 55 | dessen 56 | dich 57 | dir 58 | du 59 | dies 60 | diese 61 | diesem 62 | diesen 63 | dieser 64 | dieses 65 | doch 66 | dort 67 | durch 68 | ein 69 | eine 70 | einem 71 | einen 72 | einer 73 | eines 74 | einig 75 | einige 76 | einigem 77 | einigen 78 | einiger 79 | einiges 80 | einmal 81 | er 82 | ihn 83 | ihm 84 | es 85 | etwas 86 | euer 87 | eure 88 | eurem 89 | euren 90 | eurer 91 | eures 92 | für 93 | gegen 94 | gewesen 95 | hab 96 | habe 97 | haben 98 | hat 99 | hatte 100 | hatten 101 | hier 102 | hin 103 | hinter 104 | ich 105 | mich 106 | mir 107 | ihr 108 | ihre 109 | ihrem 110 | ihren 111 | ihrer 112 | ihres 113 | euch 114 | im 115 | in 116 | indem 117 | ins 118 | ist 119 | jede 120 | jedem 121 | jeden 122 | jeder 123 | jedes 124 | jene 125 | jenem 126 | jenen 127 | jener 128 | jenes 129 | jetzt 130 | kann 131 | kein 132 | keine 133 | keinem 134 | keinen 135 | keiner 136 | keines 137 | können 138 | könnte 139 | machen 140 | man 141 | manche 142 | manchem 143 | manchen 144 | mancher 145 | manches 146 | mein 147 | meine 148 | meinem 149 | meinen 150 | meiner 151 | meines 152 | mit 153 | muss 154 | musste 155 | nach 156 | nicht 157 | nichts 158 | noch 159 | nun 160 | nur 161 | ob 162 | oder 163 | ohne 164 | sehr 165 | sein 166 | seine 167 | seinem 168 | seinen 169 | seiner 170 | seines 171 | selbst 172 | sich 173 | sie 174 | ihnen 175 | sind 176 | so 177 | solche 178 | solchem 179 | solchen 180 | solcher 181 | solches 182 | soll 183 | sollte 184 | sondern 185 | sonst 186 | über 187 | um 188 | und 189 | uns 190 | unse 191 | unsem 192 | unsen 193 | unser 194 | unses 195 | unter 196 | viel 197 | vom 198 | von 199 | vor 200 | während 201 | war 202 | waren 203 | warst 204 | was 205 | weg 206 | weil 207 | weiter 208 | welche 209 | welchem 210 | welchen 211 | welcher 212 | welches 213 | wenn 214 | werde 215 | werden 216 | wie 217 | wieder 218 | will 219 | wir 220 | wird 221 | wirst 222 | wo 223 | wollen 224 | wollte 225 | würde 226 | würden 227 | zu 228 | zum 229 | zur 230 | zwar 231 | zwischen 232 | 233 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Solr/Query/Modifier/Faceting.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Asm_Solr_Model_Solr_Query_Modifier_Faceting 11 | { 12 | 13 | /** 14 | * @var Mage_Catalog_Model_Resource_Eav_Attribute[] 15 | */ 16 | protected $filterableAttributes; 17 | 18 | /** 19 | * Called before a query is executed. Modifies the query to add faceting 20 | * parameters. 21 | * 22 | * @param Varien_Event_Observer $observer Observer/event data 23 | */ 24 | public function modifyQuery(Varien_Event_Observer $observer) 25 | { 26 | /** @var Asm_Solr_Model_Solr_Query $query */ 27 | $query = $observer->getQuery(); 28 | $query->setFaceting(); 29 | 30 | $this->filterableAttributes = $this->getFilterableAttributes(); 31 | 32 | // set facet.* query parameters / which facets to generate 33 | foreach ($this->filterableAttributes as $attribute) { 34 | $query->addFacetField(Mage::helper('solr/schema')->getFieldNameByAttribute($attribute)); 35 | } 36 | 37 | // set filter query (fq) parameters / actually filtering results 38 | $filters = $this->getQueryFilters(); 39 | foreach ($filters as $fieldName => $value) { 40 | $query->addFilter($fieldName, $value); 41 | } 42 | } 43 | 44 | /** 45 | * Get collection of all filterable attributes for layer products set 46 | * 47 | * @return Mage_Catalog_Model_Resource_Product_Attribute_Collection 48 | */ 49 | protected function getFilterableAttributes() 50 | { 51 | $collection = Mage::getResourceModel('catalog/product_attribute_collection'); 52 | $collection 53 | ->setItemObjectClass('catalog/resource_eav_attribute') 54 | ->addStoreLabel(Mage::app()->getStore()->getId()) 55 | ->setOrder('position', 'ASC') 56 | ->addIsFilterableFilter(); 57 | $collection->load(); 58 | 59 | return $collection; 60 | } 61 | 62 | /** 63 | * Generate filters from current URL query 64 | * 65 | * @return array Filters as field name => value pairs 66 | */ 67 | protected function getQueryFilters() 68 | { 69 | $filters = array(); 70 | $helper = Mage::helper('solr'); 71 | 72 | // get query part from current URL 73 | $urlQuery = Mage::getModel('core/url')->getRequest()->getQuery(); 74 | 75 | foreach ($this->filterableAttributes as $attribute) { 76 | $attributeCode = $attribute->getAttributeCode(); 77 | 78 | // match attribute codes 79 | if (array_key_exists($attributeCode, $urlQuery)) { 80 | // generate filters from matches 81 | $fieldName = Mage::helper('solr/schema')->getFieldNameByAttribute($attribute); 82 | $filters[$fieldName] = '"' . $urlQuery[$attributeCode] . '"'; 83 | } 84 | } 85 | 86 | return $filters; 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Indexer/Catalog.php: -------------------------------------------------------------------------------- 1 | array( 14 | Mage_Index_Model_Event::TYPE_SAVE, 15 | Mage_Index_Model_Event::TYPE_MASS_ACTION, 16 | Mage_Index_Model_Event::TYPE_REINDEX 17 | ), 18 | ); 19 | 20 | 21 | protected function _construct() 22 | { 23 | $this->_init('solr/indexer_catalog'); 24 | } 25 | 26 | /** 27 | * Get Indexer name 28 | * 29 | * @return string 30 | */ 31 | public function getName() 32 | { 33 | return Mage::helper('solr')->__('Solr Catalog Search Index'); 34 | } 35 | 36 | /** 37 | * Get Indexer description 38 | * 39 | * @return string 40 | */ 41 | public function getDescription() 42 | { 43 | return Mage::helper('solr')->__('Rebuild Solr catalog search index'); 44 | } 45 | 46 | /** 47 | * Register indexer required data inside event object 48 | * 49 | * @param Mage_Index_Model_Event $event 50 | */ 51 | protected function _registerEvent(Mage_Index_Model_Event $event) 52 | { 53 | // TODO: Implement _registerEvent() method. 54 | 55 | $event->addNewData(self::EVENT_MATCH_RESULT_KEY, true); 56 | switch ($event->getEntity()) { 57 | case Mage_Catalog_Model_Product::ENTITY: 58 | $this->registerCatalogProductEvent($event); 59 | break; 60 | // TODO handle other models 61 | } 62 | } 63 | 64 | protected function registerCatalogProductEvent(Mage_Index_Model_Event $event) 65 | { 66 | switch ($event->getType()) { 67 | case Mage_Index_Model_Event::TYPE_SAVE: 68 | $product = $event->getDataObject(); 69 | /** @var Mage_Catalog_Model_Product $product */ 70 | $event->addNewData('solr_update_product_id', $product->getId()); 71 | break; 72 | case Mage_Index_Model_Event::TYPE_MASS_ACTION: 73 | 74 | 75 | break; 76 | 77 | } 78 | } 79 | 80 | /** 81 | * Process event based on event state data 82 | * 83 | * @param Mage_Index_Model_Event $event 84 | */ 85 | protected function _processEvent(Mage_Index_Model_Event $event) 86 | { 87 | // TODO: Implement _processEvent() method. 88 | 89 | // if ($event->getData('solr_update_product_id')) 90 | // { 91 | // $this->callEventHandler($event); 92 | // } 93 | 94 | 95 | } 96 | 97 | public function reindexAll() 98 | { 99 | $resource = $this->getResource(); 100 | /** @var Asm_Solr_Model_Resource_Indexer_Catalog $resource */ 101 | $resource->rebuildIndex(); 102 | 103 | 104 | 105 | $connection = Mage::helper('solr/connectionManager')->getConnection(); 106 | /** @var $connection Asm_Solr_Model_Solr_Connection */ 107 | 108 | $connection->commit(); 109 | } 110 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/HttpTransportException.php: -------------------------------------------------------------------------------- 1 | 37 | */ 38 | 39 | class Apache_Solr_HttpTransportException extends Apache_Solr_Exception 40 | { 41 | /** 42 | * SVN Revision meta data for this class 43 | */ 44 | const SVN_REVISION = '$Revision$'; 45 | 46 | /** 47 | * SVN ID meta data for this class 48 | */ 49 | const SVN_ID = '$Id$'; 50 | 51 | /** 52 | * Response for which exception was generated 53 | * 54 | * @var Apache_Solr_Response 55 | */ 56 | private $_response; 57 | 58 | /** 59 | * HttpTransportException Constructor 60 | * 61 | * @param Apache_Solr_Response $response 62 | */ 63 | public function __construct(Apache_Solr_Response $response) 64 | { 65 | parent::__construct("'{$response->getHttpStatus()}' Status: {$response->getHttpStatusMessage()}", $response->getHttpStatus()); 66 | 67 | $this->_response = $response; 68 | } 69 | 70 | /** 71 | * Get the response for which this exception was generated 72 | * 73 | * @return Apache_Solr_Response 74 | */ 75 | public function getResponse() 76 | { 77 | return $this->_response; 78 | } 79 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Solr/Query/Modifier/Sorting.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class Asm_Solr_Model_Solr_Query_Modifier_Sorting 12 | { 13 | 14 | /** 15 | * 16 | * 17 | * @var array 18 | */ 19 | protected $sortAttributes = array(); 20 | 21 | /** 22 | * Sort directions whitelist 23 | * 24 | * @var array 25 | */ 26 | protected $directions = array('asc', 'desc'); 27 | 28 | 29 | /** 30 | * Init sorting query modifier 31 | * 32 | */ 33 | public function __construct() 34 | { 35 | $availableSortAttributes = $this->getCatalogConfig()->getAttributeUsedForSortByArray(); 36 | unset($availableSortAttributes['position']); 37 | 38 | $this->sortAttributes = array_merge(array( 39 | 'relevance' => 'Relevance' // the actual label does not matter here, we're only using the keys 40 | ), $availableSortAttributes); 41 | 42 | // unset the sort order session data to have the sort select box always 43 | // reflect the correct sort field and reset it when starting a new search 44 | Mage::getSingleton('catalog/session')->unsetData('sort_order'); 45 | } 46 | 47 | /** 48 | * Called before a query is executed. Modifies the query to add sorting 49 | * parameters. 50 | * 51 | * @param Varien_Event_Observer $observer Observer/event data 52 | * @throws InvalidArgumentException for invalid dir and order parameters 53 | */ 54 | public function modifyQuery(Varien_Event_Observer $observer) 55 | { 56 | // get query part from current URL 57 | $urlQuery = Mage::getModel('core/url')->getRequest()->getQuery(); 58 | 59 | if (!array_key_exists('order', $urlQuery)) { 60 | return; 61 | } 62 | 63 | $direction = $urlQuery['dir']; 64 | $field = $urlQuery['order']; 65 | 66 | if (!in_array($direction, $this->directions)) { 67 | throw new InvalidArgumentException( 68 | 'Invalid sort direction parameter "' . $direction . '", must be asc or desc.', 69 | 1397868721 70 | ); 71 | } 72 | 73 | if (array_key_exists($field, $this->sortAttributes)) { 74 | if ($field == 'relevance') { 75 | $sortField = 'relevance'; 76 | } else if ($field == 'name') { 77 | $sortField = 'sortTitle'; 78 | } else { 79 | $sortField = Mage::helper('solr/schema')->getFieldNameByAttribute($field); 80 | } 81 | 82 | /** @var Asm_Solr_Model_Solr_Query $query */ 83 | $query = $observer->getQuery(); 84 | $query->setSorting($sortField . ' ' . $direction); 85 | } else { 86 | throw new InvalidArgumentException( 87 | 'No sortable attribute found for sort parameter ' . $field, 88 | 1397868606 89 | ); 90 | } 91 | } 92 | 93 | /** 94 | * Retrieve Catalog Config object 95 | * 96 | * @return Mage_Catalog_Model_Config 97 | */ 98 | protected function getCatalogConfig() { 99 | return Mage::getSingleton('catalog/config'); 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Solr/Response.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Asm_Solr_Model_Solr_Response 11 | { 12 | /** 13 | * @var Apache_Solr_Response 14 | */ 15 | protected $rawResponse; 16 | 17 | /** 18 | * Constructor 19 | * 20 | */ 21 | public function __construct(array $responseParameters = array()) 22 | { 23 | $this->rawResponse = $responseParameters['rawResponse']; 24 | } 25 | 26 | /** 27 | * Gets the number of results found 28 | * 29 | * @return integer Number of results found 30 | */ 31 | public function getNumberOfResults() 32 | { 33 | return $this->rawResponse->response->numFound; 34 | } 35 | 36 | /** 37 | * Gets the result documents 38 | * 39 | * @return Apache_Solr_Document[] Array of Apache_Solr_Document 40 | */ 41 | public function getDocuments() 42 | { 43 | return $this->rawResponse->response->docs; 44 | } 45 | 46 | /** 47 | * Gets the result highlighting 48 | * 49 | * @return Apache_Solr_Document[] Array of Apache_Solr_Document 50 | */ 51 | public function getHighlighting() 52 | { 53 | return $this->rawResponse->highlighting; 54 | } 55 | 56 | public function getFacetRanges() 57 | { 58 | return $this->rawResponse->facet_counts->facet_ranges; 59 | } 60 | 61 | public function getFacetField() 62 | { 63 | return $this->rawResponse->facet_counts->facet_fields; 64 | } 65 | 66 | /** 67 | * Gets the field facets 68 | * 69 | * @return Asm_Solr_Model_Solr_Facet_Facet[] Array of Asm_Solr_Model_Solr_Facet_Facet 70 | */ 71 | public function getFacetFields() 72 | { 73 | $facets = array(); 74 | $facetFields = $this->rawResponse->facet_counts->facet_fields; 75 | $fieldToAttributeMap = Mage::helper('solr/schema')->getFieldToAttributeMap(); 76 | 77 | foreach ($facetFields as $facetField => $facetOptions) { 78 | $attributeCode = $facetField; 79 | if (!array_key_exists($facetField, $fieldToAttributeMap)) { 80 | // remove field type suffix 81 | $attributeCode = implode('_', explode('_', $facetField, -1)); 82 | } 83 | 84 | $facet = Mage::getModel('solr/solr_facet_facet') 85 | ->setName($attributeCode) 86 | ->setAttributeCode($attributeCode) 87 | ->setField($facetField); 88 | 89 | if (!empty($facetOptions)) { 90 | foreach ($facetOptions as $optionValue => $numberOfResults) { 91 | $facetOption = Mage::getModel('solr/solr_facet_facetOption') 92 | ->setFacet($facet) 93 | ->setLabel($optionValue) 94 | ->setValue($optionValue) 95 | ->setNumberOfResults($numberOfResults); 96 | $facet->addOption($facetOption); 97 | } 98 | } 99 | 100 | $facets[] = $facet; 101 | } 102 | 103 | return $facets; 104 | } 105 | 106 | /* TODO add range and query facet support 107 | public function getFacetRanges() 108 | { 109 | 110 | } 111 | 112 | public function getFacetQueries() 113 | { 114 | 115 | } 116 | */ 117 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Solr/Facet/Facet.php: -------------------------------------------------------------------------------- 1 | 10 | * 11 | * @method string getName() 12 | * @method Asm_Solr_Model_Solr_Facet_Facet setName(string $name) 13 | * @method Asm_Solr_Model_Solr_Facet_Facet setAttributeCode(string $attributeCode) 14 | * @method Asm_Solr_Model_Solr_Facet_Facet setField(string $field) 15 | */ 16 | class Asm_Solr_Model_Solr_Facet_Facet extends Varien_Object 17 | { 18 | const TYPE_FIELD = 'field'; 19 | 20 | const TYPE_QUERY = 'query'; 21 | 22 | const TYPE_RANGE = 'range'; 23 | 24 | /** 25 | * Facet type, defaults to field facet. 26 | * 27 | * @var string 28 | */ 29 | protected $type = self::TYPE_FIELD; 30 | 31 | /** 32 | * Facet options 33 | * 34 | * @var array 35 | */ 36 | protected $options = array(); 37 | 38 | 39 | 40 | /** 41 | * Checks whether an option of the facet has been selected by the user by 42 | * checking the URL GET parameters. 43 | * 44 | * @return boolean TRUE if any option of the facet is applied, FALSE otherwise 45 | */ 46 | public function isActive() 47 | { 48 | $isActive = FALSE; 49 | 50 | $selectedOptions = $this->getSelectedOptions(); 51 | if (!empty($selectedOptions)) { 52 | $isActive = TRUE; 53 | } 54 | 55 | return $isActive; 56 | } 57 | 58 | /** 59 | * Gets the facet's currently user-selected options 60 | * 61 | * @return array An array with user-selected facet options. 62 | */ 63 | public function getSelectedOptions() 64 | { 65 | $selectedOptions = array(); 66 | 67 | #FIXME 68 | $resultParameters = t3lib_div::_GET('tx_solr'); 69 | $filterParameters = array(); 70 | if (isset($resultParameters['filter'])) { 71 | $filterParameters = (array)array_map('urldecode', $resultParameters['filter']); 72 | } 73 | 74 | foreach ($filterParameters as $filter) { 75 | list($facetName, $filterValue) = explode(':', $filter); 76 | 77 | if ($facetName == $this->name) { 78 | $selectedOptions[] = $filterValue; 79 | } 80 | } 81 | 82 | return $selectedOptions; 83 | } 84 | 85 | /** 86 | * Adds a facet option 87 | * 88 | * @param Asm_Solr_Model_Solr_Facet_FacetOption $option 89 | */ 90 | public function addOption(Asm_Solr_Model_Solr_Facet_FacetOption $option) { 91 | $this->options[$option->getValue()] = $option; 92 | } 93 | 94 | public function getOptions() { 95 | return $this->options; 96 | } 97 | 98 | /** 99 | * Gets an option by its value 100 | * 101 | * @param string $optionValue Option value 102 | * @return Asm_Solr_Model_Solr_Facet_FacetOption 103 | */ 104 | public function getOption($optionValue) 105 | { 106 | return $this->options[$optionValue]; 107 | } 108 | 109 | /** 110 | * Gets the facet's internal type. One of field, range, or query. 111 | * 112 | * @return string Facet type. 113 | */ 114 | public function getType() 115 | { 116 | return $this->type; 117 | } 118 | 119 | /** 120 | * Gets the facet's label 121 | * 122 | * @return string 123 | */ 124 | public function getLabel() 125 | { 126 | return $this->getName(); 127 | } 128 | 129 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Helper/ConnectionManager.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class Asm_Solr_Helper_ConnectionManager 13 | { 14 | 15 | /** 16 | * Gets the default connection / connection for the current store 17 | * 18 | * @return Asm_Solr_Model_Solr_Connection 19 | */ 20 | public function getConnection() 21 | { 22 | // get current store Solr configuration 23 | $solrConnectionParameters = Mage::getStoreConfig('solr/connection'); 24 | $connection = Mage::getModel('solr/solr_connection', $solrConnectionParameters); 25 | 26 | return $connection; 27 | } 28 | 29 | /** 30 | * Gets the Solr connection for a specific store 31 | * 32 | * @param string|int $store Store code or store ID 33 | * @return Asm_Solr_Model_Solr_Connection 34 | */ 35 | public function getConnectionByStore($store) 36 | { 37 | $storeCode = $store; 38 | 39 | if (is_numeric($store)) { 40 | $storeCode = Mage::getModel('core/store') 41 | ->getCollection() 42 | ->addIdFilter($store) 43 | ->getFirstItem() 44 | ->getCode(); 45 | } 46 | 47 | $solrConnectionParameters = Mage::getConfig()->getNode('solr/connection', 'stores', $storeCode); 48 | $connection = Mage::getModel('solr/solr_connection', $solrConnectionParameters); 49 | 50 | return $connection; 51 | } 52 | 53 | /** 54 | * Gets the Solr connections for all stores of a website. 55 | * 56 | * @param int|string $site Website code or ID 57 | * @return Asm_Solr_Model_Solr_Connection[] Array of Asm_Solr_Model_Solr_Connection objects 58 | */ 59 | public function getConnectionsBySite($site) 60 | { 61 | $connections = array(); 62 | 63 | if (is_string($site) && !is_numeric($site)) { 64 | // website code 65 | $collection = Mage::getModel('core/website')->getCollection()->addFieldToFilter('code', $site); 66 | $website = $collection->getFirstItem(); 67 | } else { 68 | // website id 69 | $website = Mage::getModel('core/website')->getCollection()->addIdFilter($site)->getFirstItem(); 70 | } 71 | 72 | $stores = $website->getStoreCollection(); 73 | foreach ($stores as $store) { 74 | /** @var Mage_Core_Model_Store $store */ 75 | $storeConnection = $this->getConnectionByStore($store->getId()); 76 | $connections[] = $storeConnection; 77 | } 78 | 79 | return $connections; 80 | } 81 | 82 | /** 83 | * Gets all connections for all websites and their stores 84 | * 85 | * @return Asm_Solr_Model_Solr_Connection[] 86 | */ 87 | public function getAllConnections() 88 | { 89 | $connections = array(); 90 | $websites = Mage::getModel('core/website')->getCollection()->addFieldToFilter('website_id', array("neq" => '0')); 91 | 92 | foreach ($websites as $website) { 93 | /** @var Mage_Core_Model_Website $website */ 94 | $id = $website->getId(); 95 | $websiteConnections = $this->getConnectionsBySite($website->getId()); 96 | $connections = array_merge($connections, $websiteConnections); 97 | } 98 | 99 | return $connections; 100 | } 101 | 102 | } 103 | 104 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Facets.php: -------------------------------------------------------------------------------- 1 | getChildHtml('solr.filters_applied'); 20 | } 21 | 22 | // /** 23 | // * Whether to show the number of products that will be returned when 24 | // * applying a filter. 25 | // * 26 | // * @return bool 27 | // */ 28 | // public function shouldDisplayProductCount() 29 | // { 30 | // if ($this->displayProductCount === null) { 31 | // $this->displayProductCount = Mage::helper('catalog')->shouldDisplayProductCountOnLayer(); 32 | // } 33 | // 34 | // return $this->displayProductCount; 35 | // } 36 | 37 | /** 38 | * Gets the facets returned for the current/last query 39 | * 40 | * @return Asm_Solr_Model_Solr_Facet_Facet[] 41 | */ 42 | public function getFacets() { 43 | $result = Mage::registry('solr_result'); 44 | $facets = $this->getResult()->getResponse()->getFacetFields(); 45 | 46 | return $facets; 47 | } 48 | 49 | public function getKeywords() 50 | { 51 | if (!$this->getData('keywords')) 52 | { 53 | $keywords = $this->getRequest()->getParam('q', ''); 54 | 55 | $this->setData('keywords', $keywords); 56 | } 57 | 58 | return $this->getData('keywords'); 59 | } 60 | 61 | public function getFilterQuery() 62 | { 63 | if (!$this->getData('filter_query')) 64 | { 65 | $keywords = $this->getRequest()->getParam('fq', ''); 66 | 67 | $this->setData('filter_query', $keywords); 68 | } 69 | 70 | return $this->getData('filter_query'); 71 | } 72 | 73 | public function getResult() 74 | { 75 | // if we don't have a result set for us, let's make one 76 | if (!$this->getData('result')) 77 | { 78 | /** @var Asm_Solr_Model_Result $result */ 79 | $result = Mage::getModel('solr/result'); 80 | 81 | $keywords = $this->getKeywords(); 82 | $filterQuery = $this->getFilterQuery(); 83 | $solrType = 'catalog/product'; 84 | $limit = 0; 85 | $offset = 0; 86 | 87 | $query = $result->getQuery(); 88 | $query->setFaceting(true); 89 | $query->setKeywords($keywords); 90 | $query->addFilter('type', $solrType); 91 | 92 | $query->addQueryParameter("facet.range","price"); 93 | $query->addQueryParameter("facet.range.start","0"); 94 | $query->addQueryParameter("facet.range.end","100"); 95 | $query->addQueryParameter("facet.range.gap","10"); 96 | 97 | $result->load($limit, $offset); 98 | 99 | $this->setData('result', $result); 100 | } 101 | 102 | return $this->getData('result'); 103 | } 104 | 105 | public function getFacetRanges() 106 | { 107 | return $this->getResult()->getResponse()->getFacetRanges(); 108 | } 109 | } -------------------------------------------------------------------------------- /lib/Apache/Solr/HttpTransport/Abstract.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * Convenience class that implements the transport implementation. Can be extended by 41 | * real implementations to do some of the common book keeping 42 | */ 43 | abstract class Apache_Solr_HttpTransport_Abstract implements Apache_Solr_HttpTransport_Interface 44 | { 45 | /** 46 | * Our default timeout value for requests that don't specify a timeout 47 | * 48 | * @var float 49 | */ 50 | private $_defaultTimeout = false; 51 | 52 | /** 53 | * Get the current default timeout setting (initially the default_socket_timeout ini setting) 54 | * in seconds 55 | * 56 | * @return float 57 | */ 58 | public function getDefaultTimeout() 59 | { 60 | // lazy load the default timeout from the ini settings 61 | if ($this->_defaultTimeout === false) 62 | { 63 | $this->_defaultTimeout = (int) ini_get('default_socket_timeout'); 64 | 65 | // double check we didn't get 0 for a timeout 66 | if ($this->_defaultTimeout <= 0) 67 | { 68 | $this->_defaultTimeout = 60; 69 | } 70 | } 71 | 72 | return $this->_defaultTimeout; 73 | } 74 | 75 | /** 76 | * Set the current default timeout for all HTTP requests 77 | * 78 | * @param float $timeout 79 | */ 80 | public function setDefaultTimeout($timeout) 81 | { 82 | $timeout = (float) $timeout; 83 | 84 | if ($timeout >= 0) 85 | { 86 | $this->_defaultTimeout = $timeout; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/result/page/list.phtml: -------------------------------------------------------------------------------- 1 | 27 | 34 | getCollection(); 36 | $_helper = $this->helper('catalog/output'); 37 | ?> 38 | count()): ?> 39 |

__('There are no pages matching your search terms.') ?>

40 | 41 |
42 | getToolbarHtml() ?> 43 | 44 | 45 |
    46 | 47 |
  1. 48 |
    49 |
    50 | stripTags($_page->getTitle(), null, true); ?> 51 |

    52 | 53 | getTitle() ?> 54 | 55 |

    56 |
    57 | getHighlights() as $highlight): ?> 58 |

    59 | 60 | __('Read More') ?> 61 |
    62 |
    63 |
    64 |
  2. 65 | 66 |
67 | 68 | 69 |
70 | getToolbarHtml() ?> 71 |
72 |
73 | 74 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Observer/Index/Page.php: -------------------------------------------------------------------------------- 1 | getPage(); 28 | $storeId = $event->getStoreId(); 29 | $pageId = $page->getId(); 30 | $pageContent = $page->getContent(); 31 | $links = array(); 32 | $allowedFileTypes = $this->getAllowedFileTypesRegex(); 33 | 34 | preg_match_all('/.*<\/a>/iU', $pageContent, $links); 35 | 36 | if (empty($links[0])) { 37 | // bail out if we didn't find any file links, save a couple CPU cycles 38 | return; 39 | } 40 | 41 | $foundFiles = array(); 42 | $trackedFiles = $this->getTrackedFilesForPage($pageId); 43 | 44 | $numberOfFiles = count($links[0]); 45 | for ($i = 0; $i < $numberOfFiles; $i++) { 46 | $foundFiles[] = '/' . htmlspecialchars_decode($links[1][$i]) . '.' . $links[2][$i]; 47 | } 48 | 49 | $addedFiles = array_diff($foundFiles, $trackedFiles); 50 | $removedFiles = array_diff($trackedFiles, $foundFiles); 51 | 52 | $this->addFilesToTracking($storeId, $pageId, $addedFiles); 53 | $this->removeFilesFromTracking($storeId, $pageId, $removedFiles); 54 | } 55 | 56 | protected function addFilesToTracking($storeId, $pageId, $addedFiles) 57 | { 58 | foreach ($addedFiles as $addedFile) { 59 | $indexQueueFile = Mage::getModel('solr/indexqueue_file')->setData(array( 60 | 'cms_page_id' => $pageId, 61 | 'store_id' => $storeId, 62 | 'file_path' => $addedFile, 63 | 'changed' => time(), 64 | 'indexed' => 0 65 | )); 66 | $indexQueueFile->save(); 67 | } 68 | } 69 | 70 | protected function removeFilesFromTracking($storeId, $pageId, $removedFiles) 71 | { 72 | 73 | // FIXME must filter by pageId, possibly also storeId 74 | // file might still be linked from another page 75 | 76 | foreach ($removedFiles as $removedFile) { 77 | $indexQueueFile = Mage::getModel('solr/indexqueue_file')->load( 78 | $removedFile, 79 | 'file_path' 80 | ); 81 | $indexQueueFile->delete(); 82 | } 83 | } 84 | 85 | protected function getAllowedFileTypesRegex() { 86 | $fileTypes = Mage::getStoreConfig('index/files/file_types'); 87 | 88 | $fileTypes = trim($fileTypes); 89 | $fileTypes = str_replace(' ', '', $fileTypes); 90 | $fileTypes = str_replace(',', '|', $fileTypes); 91 | 92 | return "($fileTypes)"; 93 | } 94 | 95 | protected function getTrackedFilesForPage($pageId) 96 | { 97 | $trackedFiles = Mage::getModel('solr/indexqueue_file') 98 | ->getCollection() 99 | ->addFieldToFilter('cms_page_id', $pageId); 100 | 101 | $trackedFiles = $trackedFiles->getColumnValues('file_path'); 102 | 103 | return $trackedFiles; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/result/file/list.phtml: -------------------------------------------------------------------------------- 1 | 27 | 34 | getCollection(); 36 | $_helper = $this->helper('catalog/output'); 37 | ?> 38 | count()): ?> 39 |

__('There are no pages matching your search terms.') ?>

40 | 41 |
42 | getToolbarHtml() ?> 43 | 44 | 45 |
    46 | 47 |
  1. 48 | 64 |
  2. 65 | 66 |
67 | 68 | 69 |
70 | getToolbarHtml() ?> 71 |
72 |
73 | 74 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/File.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class Asm_Solr_Model_Resource_Indexer_File extends Mage_Core_Model_Resource_Db_Abstract 28 | { 29 | 30 | /** 31 | * Resource initialization 32 | */ 33 | protected function _construct() { 34 | $this->_setResource('core'); 35 | } 36 | 37 | /** 38 | * Rebuild the index for all stores at once or just one specific store. 39 | * 40 | * @param int|null $storeId Store to re-index single int store ID to re-index single store, null to re-index all stores 41 | */ 42 | public function rebuildIndex($storeId = null) 43 | { 44 | if (is_null($storeId)) { 45 | // re-index all stores 46 | $storeIds = array_keys(Mage::app()->getStores()); 47 | foreach ($storeIds as $storeId) { 48 | $this->rebuildStoreIndex($storeId); 49 | } 50 | } else { 51 | // re-index specific store 52 | $this->rebuildStoreIndex($storeId); 53 | } 54 | } 55 | 56 | public function rebuildStoreIndex($storeId) 57 | { 58 | $files = $this->getFilesByStore($storeId); 59 | $solr = Mage::helper('solr/connectionManager')->getConnectionByStore($storeId); 60 | 61 | foreach ($files as $file) { 62 | $document = $this->buildFileDocument($storeId, $file); 63 | $solr->addDocument($document); 64 | } 65 | } 66 | 67 | 68 | protected function getFilesByStore($storeId) 69 | { 70 | $collection = Mage::getModel('solr/indexqueue_file')->getCollection() 71 | ->addFilter('store_id', $storeId) 72 | ->load(); 73 | 74 | return $collection; 75 | } 76 | 77 | /** 78 | * Build a Solr document for a specific file 79 | * 80 | * @param integer $storeId Store ID the file belongs to/where it is linked on a page 81 | * @param Asm_Solr_Model_Indexqueue_File $file The file to index 82 | * @return Apache_Solr_Document 83 | */ 84 | protected function buildFileDocument($storeId, Asm_Solr_Model_Indexqueue_File $file) 85 | { 86 | $helper = Mage::helper('solr'); 87 | $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); 88 | $host = parse_url($baseUrl, PHP_URL_HOST); 89 | 90 | $document = new Apache_Solr_Document(); 91 | 92 | $document->setField('appKey', 'Asm_Solr'); 93 | $document->setField('type', 'solr/indexqueue_file'); 94 | 95 | $document->setField('id', $helper->getFileDocumentId($file->getId())); 96 | $document->setField('site', $host); 97 | $document->setField('siteHash', $helper->getSiteHashForDomain($host)); 98 | $document->setField('storeId', $storeId); 99 | $document->setField('changed', $helper->dateToIso($file->getFileLastChangedTime())); 100 | 101 | $document->setField('productId', 0); 102 | $document->setField('sku', 'solr/indexqueue_file'); 103 | 104 | $document->setField('title', $file->getName()); 105 | $document->setField('content', $file->getContent()); 106 | $document->setField('url', $file->getUrl()); 107 | 108 | return $document; 109 | } 110 | 111 | 112 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/currency.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/controllers/Adminhtml/Solr/System/Config/UtilityController.php: -------------------------------------------------------------------------------- 1 | getRequest()->getParam('section'); 17 | $website = $this->getRequest()->getParam('website'); 18 | $store = $this->getRequest()->getParam('store'); 19 | 20 | $logger = Mage::helper('solr')->getLogger(); 21 | $logger->debug("section: $section, website: $website, store: $store"); 22 | 23 | if (!empty($section) && empty($website) && empty($store)) { 24 | // empty ALL indexes 25 | 26 | $connections = Mage::helper('solr/connectionManager')->getAllConnections(); 27 | foreach ($connections as $connection) { 28 | /** @var $connection Asm_Solr_Model_Solr_Connection */ 29 | $connection->deleteByQuery('*:*'); 30 | $result = ($connection->commit()->getHttpStatus() == 200); 31 | 32 | if ($result) { 33 | $logger->info('Empty index', array('core' => $connection->getPath())); 34 | } else { 35 | $logger->error('Empty index', array('core' => $connection->getPath())); 36 | break; 37 | } 38 | } 39 | } elseif (!empty($section) && !empty($website) && empty($store)) { 40 | // empty a website's stores' indexes 41 | 42 | $connections = Mage::helper('solr/connectionManager')->getConnectionsBySite($website); 43 | foreach ($connections as $connection) { 44 | /** @var $connection Asm_Solr_Model_Solr_Connection */ 45 | $connection->deleteByQuery('*:*'); 46 | $result = ($connection->commit()->getHttpStatus() == 200); 47 | 48 | if ($result) { 49 | $logger->info('Empty index', array('core' => $connection->getPath())); 50 | } else { 51 | $logger->error('Empty index', array('core' => $connection->getPath())); 52 | break; 53 | } 54 | } 55 | } else { 56 | // empty a specific store's index 57 | 58 | $connection = Mage::helper('solr/connectionManager')->getConnectionByStore($store); 59 | $connection->deleteByQuery('*:*'); 60 | $result = ($connection->commit()->getHttpStatus() == 200); 61 | 62 | if ($result) { 63 | $logger->info('Empty index', array('core' => $connection->getPath())); 64 | } else { 65 | $logger->error('Empty index', array('core' => $connection->getPath())); 66 | } 67 | } 68 | 69 | echo $result ? 1 : 0; 70 | } 71 | 72 | public function syncsynonymsAction() { 73 | $result = true; 74 | 75 | // FIXME does not support per store synonyms yet, syncing for all 76 | $connections = Mage::helper('solr/connectionManager')->getAllConnections(); 77 | $connection = $connections[0]; 78 | 79 | $synonymHandler = Mage::getModel('solr/synonymHandler'); 80 | 81 | // remove existing synonyms 82 | $oldSynonyms = $connection->getSynonyms(); 83 | $oldSynonyms = (array) $oldSynonyms; 84 | $oldSynonyms = array_keys($oldSynonyms); 85 | foreach ($oldSynonyms as $synonym) { 86 | $connection->deleteSynonym($synonym); 87 | } 88 | 89 | // write new synonyms 90 | $queries = Mage::getModel('catalogsearch/query') 91 | ->getCollection() 92 | ->addFieldToFilter('synonym_for', array("notnull"=>true)); 93 | 94 | foreach ($queries as $query) { 95 | $baseWord = $query->getData('query_text'); 96 | $synonyms = $query->getData('synonym_for'); 97 | 98 | if (!empty($synonyms)) { 99 | $synonyms = trim($synonyms, " \"\n\r\t"); 100 | $newSynonyms = Mage::helper('solr')->trimExplode(',', $synonyms); 101 | $connection->addSynonym($baseWord, $newSynonyms); 102 | } 103 | } 104 | 105 | echo $result ? 1 : 0; 106 | } 107 | 108 | /** 109 | * Action to test the configured connection (ping) depending on the current scope 110 | * 111 | */ 112 | public function testconnectionAction() 113 | { 114 | $store = $this->getRequest()->getParam('store'); 115 | $connection = Mage::helper('solr/connectionManager')->getConnectionByStore($store); 116 | 117 | echo $connection->ping() ? 1 : 0; 118 | } 119 | 120 | public function playgroundAction() 121 | { 122 | # var_dump(Mage::getConfig()->getNode('solr/connection', 'stores', 'german')); 123 | 124 | 125 | var_dump(Mage::helper('solr/connectionManager')->getConnectionByStore('german')); 126 | } 127 | 128 | } 129 | 130 | 131 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Helper/Schema.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Asm_Solr_Helper_Schema 11 | { 12 | /** 13 | * A map of Solr document fields to Magento product attributes. 14 | * 15 | * Contains all fixed indexed fields/attributes, other product attributes 16 | * are added to Solr documents using dynamic fields. 17 | * 18 | * @var array 19 | */ 20 | protected static $fieldToAttributeMap = array( 21 | 'appKey' => '', 22 | 'type' => '', 23 | 'id' => '', 24 | 'site' => '', 25 | 'siteHash' => '', 26 | 'created' => 'created_at', 27 | 'changed' => 'changed_at', 28 | 'sku' => 'sku', 29 | 'productId' => 'entity_id', 30 | 'storeId' => '', 31 | 'categoryId' => 'category_ids', 32 | 'inStock' => '', 33 | 'isSalable' => 'is_salable', 34 | 'isVisible' => 'status', 35 | 'isVisibleInCatalog' => '', 36 | 'title' => 'name', 37 | 'content' => 'description', 38 | 'keywords' => 'meta_keywords', 39 | 'url' => '', 40 | 'price' => 'price', 41 | 'manufacturer' => 'manufacturer', 42 | 'priceCurrency' => '', 43 | 'indexed' => '', 44 | '_version_' => '', 45 | 'score' => '' 46 | ); 47 | 48 | /** 49 | * A list of dynamic field suffixes. There are more dynamic field types, 50 | * but these are the ones that are currently used. 51 | * 52 | * Used to automatically map dynamic fields back to product attributes. 53 | * 54 | * @var array 55 | */ 56 | protected static $dynamicFieldSuffixes = array( 57 | 'dateS', 58 | 'doubleS', 59 | 'intS', 60 | 'stringS', 61 | 'textS' 62 | ); 63 | 64 | /** 65 | * Gets a map of Solr document fields to Magento product attributes. 66 | * 67 | * @return array 68 | */ 69 | public function getFieldToAttributeMap() { 70 | return self::$fieldToAttributeMap; 71 | } 72 | 73 | /** 74 | * Gets a list of Solr dynamic field suffixes 75 | * 76 | * @return array 77 | */ 78 | public function getDynamicFieldSuffixes() { 79 | return self::$dynamicFieldSuffixes; 80 | } 81 | 82 | /** 83 | * Generates a (dynamic) field name for a given attribute 84 | * 85 | * @param string|Mage_Eav_Model_Entity_Attribute_Abstract $attribute Maybe an attribute object or an attribute code 86 | * @param bool $multiValue If true, generates a multi value field name, single value if false/by default 87 | * @return bool|string Field name or false if the attribute's backend type cannot be matched to a Solr field type 88 | * @throws UnexpectedValueException if $attribute is not an attribute code or an attribute instance 89 | */ 90 | public function getFieldNameByAttribute($attribute, $multiValue = false) { 91 | $fieldName = false; 92 | 93 | if (is_string($attribute)) { 94 | $fieldName = array_search($attribute, self::$fieldToAttributeMap, true); 95 | if ($fieldName !== false) { 96 | // early return if we can find the field in the field/attribute map 97 | return $fieldName; 98 | } 99 | 100 | // turn attribute code into attribute instance 101 | $attribute = Mage::getSingleton('eav/config') 102 | ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attribute); 103 | } 104 | 105 | if (!($attribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) { 106 | throw new UnexpectedValueException( 107 | '$attribute must either be an attribute code or an instance of Mage_Eav_Model_Entity_Attribute_Abstract', 108 | 1395360135 109 | ); 110 | } 111 | 112 | $attributeCode = $attribute->getAttributeCode(); 113 | 114 | $countFieldType = 'S'; // single value 115 | if ($multiValue || $attribute->getIsConfigurable()) { 116 | $countFieldType = 'M'; 117 | } 118 | 119 | if (array_key_exists($attributeCode, self::$fieldToAttributeMap)) { 120 | return self::$fieldToAttributeMap[$attributeCode]; 121 | } 122 | 123 | switch ($attribute->getBackendType()) { 124 | case 'datetime': 125 | $fieldName = $attributeCode . '_date' . $countFieldType; 126 | break; 127 | case 'decimal': 128 | $fieldName = $attributeCode . '_double' . $countFieldType; 129 | break; 130 | case 'int': 131 | // FIXME must use correct field type if it's actually int 132 | // $fieldName = $attributeCode . '_int' . $countFieldType; 133 | $fieldName = $attributeCode . '_string' . $countFieldType; 134 | break; 135 | case 'text': 136 | case 'varchar': 137 | // TODO there might be cases when you want a string instead, 138 | // might need a configuration option 139 | $fieldName = $attributeCode . '_text' . $countFieldType; 140 | break; 141 | } 142 | 143 | return $fieldName; 144 | } 145 | 146 | } -------------------------------------------------------------------------------- /app/design/frontend/base/default/layout/solr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | empty6 36 | one_column5 37 | two_columns_left4 38 | two_columns_right4 39 | three_columns3 40 | 41 | product_list_toolbar 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Helper/ContentExtractor.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Asm_Solr_Helper_ContentExtractor 27 | { 28 | /** 29 | * Unicode ranges which should get stripped before sending a document to solr. 30 | * This is necessary if a document (PDF, etc.) contains unicode characters which 31 | * are valid in the font being used in the document but are not available in the 32 | * font being used for displaying results. 33 | * 34 | * This is often the case if PDFs are being indexed where special fonts are used 35 | * for displaying bullets, etc. Usually those bullets reside in one of the unicode 36 | * "Private Use Zones" or the "Private Use Area" (plane 15 + 16) 37 | * 38 | * @see http://en.wikipedia.org/wiki/Unicode_block 39 | * @var array 40 | */ 41 | protected $stripUnicodeRanges = array( 42 | array('FFFD', 'FFFD'), // Replacement Character (�) @see http://en.wikipedia.org/wiki/Specials_%28Unicode_block%29 43 | array('E000', 'F8FF'), // Private Use Area (part of Plane 0) 44 | array('F0000', 'FFFFF'), // Supplementary Private Use Area (Plane 15) 45 | array('100000', '10FFFF'), // Supplementary Private Use Area (Plane 16) 46 | ); 47 | 48 | 49 | /** 50 | * Returns the cleaned indexable content from HTML markup. 51 | * 52 | * The content is cleaned from HTML tags and control chars Solr could 53 | * stumble on. 54 | * 55 | * @param string $content Content to clean for indexing 56 | * @return string Indexable, cleaned content ready for indexing. 57 | */ 58 | public function getIndexableContent($content) { 59 | $content = $this->cleanContent($content); 60 | $content = html_entity_decode($content, ENT_QUOTES, 'UTF-8'); 61 | // after entity decoding we might have tags again 62 | $content = strip_tags($content); 63 | $content = trim($content); 64 | 65 | return $content; 66 | } 67 | 68 | /** 69 | * Strips control characters that cause Jetty/Solr to fail. 70 | * 71 | * @param string $content The content to sanitize 72 | * @return string The sanitized content 73 | * @see http://w3.org/International/questions/qa-forms-utf-8.html 74 | */ 75 | public function stripControlCharacters($content) { 76 | // Printable utf-8 does not include any of these chars below x7F 77 | return preg_replace('@[\x00-\x08\x0B\x0C\x0E-\x1F]@', ' ', $content); 78 | } 79 | 80 | /** 81 | * Strips a UTF-8 character range 82 | * 83 | * @param string $content Content to sanitize 84 | * @param string $start Unicode range start character as uppercase hexadecimal string 85 | * @param string $end Unicode range end character as uppercase hexadecimal string 86 | * @return string Sanitized content 87 | */ 88 | public function stripUnicodeRange($content, $start, $end) { 89 | return preg_replace('/[\x{' . $start . '}-\x{' . $end . '}]/u', '', $content); 90 | } 91 | 92 | /** 93 | * Strips unusable unicode ranges 94 | * 95 | * @param string $content Content to sanitize 96 | * @return string Sanitized content 97 | */ 98 | public function stripUnicodeRanges($content) { 99 | foreach ($this->stripUnicodeRanges as $range) { 100 | $content = $this->stripUnicodeRange($content, $range[0], $range[1]); 101 | } 102 | 103 | return $content; 104 | } 105 | 106 | /** 107 | * Strips html tags, and tab, new-line, carriage-return,   whitespace 108 | * characters. 109 | * 110 | * @param string $content String to clean 111 | * @return string String cleaned from tags and special whitespace characters 112 | */ 113 | public function cleanContent($content) { 114 | $content = $this->stripControlCharacters($content); 115 | 116 | // remove Javascript 117 | $content = preg_replace('@]*>.*?<\/script>@msi', '', $content); 118 | 119 | // remove internal CSS styles 120 | $content = preg_replace('@]*>.*?<\/style>@msi', '', $content); 121 | 122 | // prevents concatenated words when stripping tags afterwards 123 | $content = str_replace(array('<', '>'), array(' <', '> '), $content); 124 | $content = strip_tags($content); 125 | $content = str_replace(array("\t", "\n", "\r", ' '), ' ', $content); 126 | $content = $this->stripUnicodeRanges($content); 127 | $content = trim($content); 128 | 129 | return $content; 130 | } 131 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/english/schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | id 115 | 116 | -------------------------------------------------------------------------------- /lib/Apache/Solr/HttpTransport/Interface.php: -------------------------------------------------------------------------------- 1 | , Donovan Jimenez 37 | */ 38 | 39 | /** 40 | * Interface that all Transport (HTTP Requester) implementations must implement. These 41 | * Implementations can then be plugged into the Service instance in order to user their 42 | * the desired method for making HTTP requests 43 | */ 44 | interface Apache_Solr_HttpTransport_Interface 45 | { 46 | /** 47 | * Get the current default timeout for all HTTP requests 48 | * 49 | * @return float 50 | */ 51 | public function getDefaultTimeout(); 52 | 53 | /** 54 | * Set the current default timeout for all HTTP requests 55 | * 56 | * @param float $timeout 57 | */ 58 | public function setDefaultTimeout($timeout); 59 | 60 | /** 61 | * Set authentication credentials to pass along with the requests. 62 | * 63 | * These will be used to perform HTTP Basic authentication. 64 | * 65 | * @param string $username 66 | * @param string $password 67 | */ 68 | public function setAuthenticationCredentials($username, $password); 69 | 70 | /** 71 | * Perform a GET HTTP operation with an optional timeout and return the response 72 | * contents, use getLastResponseHeaders to retrieve HTTP headers 73 | * 74 | * @param string $url 75 | * @param float|boolean $timeout 76 | * @return Apache_Solr_HttpTransport_Response HTTP response 77 | */ 78 | public function performGetRequest($url, $timeout = false); 79 | 80 | /** 81 | * Perform a HEAD HTTP operation with an optional timeout and return the response 82 | * headers - NOTE: head requests have no response body 83 | * 84 | * @param string $url 85 | * @param float|boolean $timeout 86 | * @return Apache_Solr_HttpTransport_Response HTTP response 87 | */ 88 | public function performHeadRequest($url, $timeout = false); 89 | 90 | /** 91 | * Perform a POST HTTP operation with an optional timeout and return the response 92 | * contents, use getLastResponseHeaders to retrieve HTTP headers 93 | * 94 | * @param string $url 95 | * @param string $rawPost 96 | * @param string $contentType 97 | * @param float|boolean $timeout 98 | * @return Apache_Solr_HttpTransport_Response HTTP response 99 | */ 100 | public function performPostRequest($url, $rawPost, $contentType, $timeout = false); 101 | 102 | /** 103 | * Perform a PUT HTTP operation with an optional timeout and return the response 104 | * contents, use getLastResponseHeaders to retrieve HTTP headers 105 | * 106 | * @param string $url 107 | * @param string $rawPut 108 | * @param string $contentType 109 | * @param float|boolean $timeout 110 | * @return Apache_Solr_HttpTransport_Response HTTP response 111 | */ 112 | public function performPutRequest($url, $rawPut, $contentType, $timeout = false); 113 | 114 | /** 115 | * Perform a DELETE HTTP operation with an optional timeout and return the response 116 | * contents, use getLastResponseHeaders to retrieve HTTP headers 117 | * 118 | * @param string $url 119 | * @param boolean|float $timeout 120 | * @return Apache_Solr_HttpTransport_Response HTTP response 121 | */ 122 | public function performDeleteRequest($url, $timeout = false); 123 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Result/Page.php: -------------------------------------------------------------------------------- 1 | getData('result')) 20 | { 21 | /** @var Asm_Solr_Model_Result $result */ 22 | $result = Mage::getModel('solr/result'); 23 | 24 | $query = $result->getQuery(); 25 | $query->setKeywords($this->getKeywords()); 26 | $query->addFilter('type', $this->getSolrType()); 27 | $query->addFilter('storeId', $this->getStoreId()); 28 | 29 | $query->addQueryParameter('hl.requireFieldMatch','true'); 30 | $query->addQueryParameter('hl.simple.pre',''); 31 | $query->addQueryParameter('hl.simple.post',''); 32 | $query->addQueryParameter('hl.fl','content'); 33 | $query->addQueryParameter('hl.usePhraseHighlighter','true'); 34 | $query->addQueryParameter('hl','true'); 35 | 36 | 37 | $result->load($this->getLimit(), $this->getOffset()); 38 | 39 | $this->setData('result', $result); 40 | } 41 | 42 | return $this->getData('result'); 43 | } 44 | 45 | public function getCollection() 46 | { 47 | // if there aren't any results, we should return nothing.... 48 | if (!$this->getResultCount()) 49 | return Mage::getModel('cms/page')->getCollection(); 50 | 51 | if (!$this->getData('collection')) // if there isn't a page collection set... 52 | { 53 | /** @var Asm_Solr_Model_Resource_Page_Collection $collection */ 54 | $collection = Mage::getModel('solr/page')->getCollection(); 55 | $collection->setSize($this->getResultCount()); 56 | 57 | $resultDocuments = $this->getResultDocuments(); 58 | 59 | $pageIds = array(); 60 | 61 | $fieldToAttributeMap = Mage::helper('solr/schema')->getFieldToAttributeMap(); 62 | 63 | foreach ($resultDocuments as $document) 64 | { 65 | $page = Mage::getModel('cms/page'); 66 | 67 | $unmappedDocumentFields = $document->getFieldNames(); 68 | 69 | // map "core/static" Solr document fields to Magento product attributes 70 | foreach ($fieldToAttributeMap as $field => $attribute) { 71 | $page->setData($field, $document->{$field}); 72 | 73 | // remove fields that have been mapped, leaves dynamic fields 74 | $fieldKey = array_search($field, $unmappedDocumentFields); 75 | if ($fieldKey !== false) { 76 | unset($unmappedDocumentFields[$fieldKey]); 77 | } 78 | } 79 | 80 | // set up highlights... 81 | $highlight = $this->getResult()->getHighlighting($page->getData('id')); 82 | 83 | if ($highlight && property_exists($highlight,'content')) 84 | $page->setHighlights($highlight->content); 85 | 86 | $dynamicFields = array_diff( 87 | $unmappedDocumentFields, 88 | array_keys(Mage::helper('solr/schema')->getFieldToAttributeMap()) 89 | ); 90 | 91 | // map Solr dynamic fields to Magento product attributes 92 | $dynamicFieldSuffixes = Mage::helper('solr/schema')->getDynamicFieldSuffixes(); 93 | foreach ($dynamicFields as $dynamicField) { 94 | $fieldNameParts = explode('_', $dynamicField); 95 | 96 | // do we have a valid dynamic field? If so, generate attribute name, map to Solr field 97 | if (in_array($fieldNameParts[count($fieldNameParts) - 1], $dynamicFieldSuffixes)) { 98 | array_pop($fieldNameParts); 99 | $attributeName = implode('_', $fieldNameParts); 100 | $page->setData($attributeName, $document->{$dynamicField}); 101 | } 102 | } 103 | 104 | $pageIds[] = $document->pageId; 105 | 106 | $collection->addItem($page); 107 | } 108 | 109 | $collection->addFieldToFilter('entity_id', array('in' => $pageIds)); 110 | 111 | $this->setData('collection', $collection); 112 | } 113 | 114 | return $this->getData('collection'); 115 | } 116 | 117 | public function getResultListHtml() 118 | { 119 | /** @var Mage_Core_Block_Template $list */ 120 | $list = $this->getChild('result_list'); 121 | 122 | if (!$list) 123 | return ''; 124 | 125 | $list->setCollection($this->getCollection()); 126 | 127 | return $list->toHtml(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Resource/Indexer/Cms.php: -------------------------------------------------------------------------------- 1 | 25 | */ 26 | class Asm_Solr_Model_Resource_Indexer_Cms extends Mage_Core_Model_Resource_Db_Abstract 27 | { 28 | protected $excludedPages = array('no-route', 'enable-cookies'); 29 | 30 | 31 | /** 32 | * Resource initialization 33 | */ 34 | protected function _construct() { 35 | $this->_setResource('core'); 36 | } 37 | 38 | /** 39 | * Event handler for CMS page save events 40 | * 41 | * @param Mage_Index_Model_Event $event 42 | */ 43 | public function CmsPageSave(Mage_Index_Model_Event $event) { 44 | $storeIds = $event->getDataObject()->getStores(); 45 | $pageId = $event->getData('solr_update_page_id'); 46 | 47 | foreach ($storeIds as $storeId) { 48 | $this->rebuildStoreIndex($storeId, $pageId); 49 | } 50 | } 51 | 52 | /** 53 | * Rebuild the index for all stores at once or just one specific store. 54 | * 55 | * @param int|null $storeId Store to re-index single int store ID to re-index single store, null to re-index all stores 56 | * @param int|array|null $pageIds Page to re-index, single int page ID to re-index a single page, array of page IDs to re-index multiple pages, null to re-index all pages 57 | */ 58 | public function rebuildIndex($storeId = null, $pageIds = null) 59 | { 60 | if (is_null($storeId)) { 61 | // re-index all stores 62 | $storeIds = array_keys(Mage::app()->getStores()); 63 | foreach ($storeIds as $storeId) { 64 | $this->rebuildStoreIndex($storeId, $pageIds); 65 | } 66 | } else { 67 | // re-index specific store 68 | $this->rebuildStoreIndex($storeId, $pageIds); 69 | } 70 | } 71 | 72 | public function rebuildStoreIndex($storeId, $pageIds = null) 73 | { 74 | $pages = $this->getSearchablePagesByStore($storeId, $pageIds); 75 | $solr = Mage::helper('solr/connectionManager')->getConnectionByStore($storeId); 76 | 77 | foreach ($pages as $page) { 78 | $document = $this->buildPageDocument($storeId, $page); 79 | $solr->addDocument($document); 80 | 81 | Mage::dispatchEvent('solr_index_page_after', array( 82 | 'store_id' => $storeId, 83 | 'page' => $page, 84 | 'page_document' => $document 85 | )); 86 | } 87 | } 88 | 89 | protected function getSearchablePagesByStore($storeId, $pageIds = null) 90 | { 91 | $collection = Mage::getModel('cms/page')->getCollection() 92 | ->addStoreFilter($storeId) 93 | ->addFieldToFilter('is_active', 1) 94 | ->addFieldToFilter('identifier', array(array('nin' => $this->excludedPages))); 95 | 96 | if (!is_null($pageIds)) { 97 | $collection->addFieldToFilter('page_id', array(array('in' => $pageIds))); 98 | } 99 | 100 | $collection->load(); 101 | 102 | return $collection; 103 | } 104 | 105 | /** 106 | * Build a Solr document for a given page 107 | * 108 | * @param integer $storeId Store ID 109 | * @param Mage_Cms_Model_Page $page Page instance 110 | * @return Apache_Solr_Document 111 | */ 112 | protected function buildPageDocument($storeId, $page) 113 | { 114 | $helper = Mage::helper('solr'); 115 | $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); 116 | $host = parse_url($baseUrl, PHP_URL_HOST); 117 | 118 | $document = new Apache_Solr_Document(); 119 | 120 | $document->setField('appKey', 'Asm_Solr'); 121 | $document->setField('type', 'cms/page'); 122 | 123 | $document->setField('id', $helper->getPageDocumentId($page->getId())); 124 | $document->setField('site', $host); 125 | $document->setField('siteHash', $helper->getSiteHashForDomain($host)); 126 | $document->setField('storeId', $storeId); 127 | 128 | $document->setField('created', $helper->dateToIso($page->getCreationTime())); 129 | $document->setField('changed', $helper->dateToIso($page->getUpdateTime())); 130 | 131 | $document->setField('sku', 'cms/page'); 132 | $document->setField('productId', 0); 133 | $document->setField('pageId', $page->getId()); 134 | 135 | $document->setField('title', $page->getTitle()); 136 | $document->setField('content', Mage::helper('solr/contentExtractor')->getIndexableContent($page->getContent())); 137 | $document->setField('keywords', $helper->trimExplode(',', $page->getMetaKeywords(), true)); 138 | $document->setField('url', Mage::helper('cms/page')->getPageUrl($page->getId())); 139 | 140 | return $document; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Result/File.php: -------------------------------------------------------------------------------- 1 | getData('result')) 20 | { 21 | /** @var Asm_Solr_Model_Result $result */ 22 | $result = Mage::getModel('solr/result'); 23 | 24 | $query = $result->getQuery(); 25 | $query->setKeywords($this->getKeywords()); 26 | $query->addFilter('type', $this->getSolrType()); 27 | $query->addFilter('storeId', $this->getStoreId()); 28 | 29 | $query->addQueryParameter('hl.requireFieldMatch','true'); 30 | $query->addQueryParameter('hl.simple.pre',''); 31 | $query->addQueryParameter('hl.simple.post',''); 32 | $query->addQueryParameter('hl.fl','content'); 33 | $query->addQueryParameter('hl.usePhraseHighlighter','true'); 34 | $query->addQueryParameter('hl','true'); 35 | 36 | 37 | $result->load($this->getLimit(), $this->getOffset()); 38 | 39 | $this->setData('result', $result); 40 | } 41 | 42 | return $this->getData('result'); 43 | } 44 | 45 | public function getCollection() 46 | { 47 | // if there aren't any results, we should return nothing.... 48 | if (!$this->getResultCount()) 49 | return Mage::getModel('solr/indexqueue_file')->getCollection(); 50 | 51 | if (!$this->getData('collection')) // if there isn't a file collection set... 52 | { 53 | /** @var Asm_Solr_Model_Resource_file_Collection $collection */ 54 | $collection = Mage::getModel('solr/indexqueue_file')->getCollection(); 55 | $collection->setSize($this->getResultCount()); 56 | 57 | $resultDocuments = $this->getResultDocuments(); 58 | 59 | $fileIds = array(); 60 | 61 | $fieldToAttributeMap = Mage::helper('solr/schema')->getFieldToAttributeMap(); 62 | 63 | foreach ($resultDocuments as $document) 64 | { 65 | $file = Mage::getModel('solr/indexqueue_file'); 66 | 67 | $unmappedDocumentFields = $document->getFieldNames(); 68 | 69 | // map "core/static" Solr document fields to Magento product attributes 70 | foreach ($fieldToAttributeMap as $field => $attribute) { 71 | $file->setData($field, $document->{$field}); 72 | 73 | // remove fields that have been mapped, leaves dynamic fields 74 | $fieldKey = array_search($field, $unmappedDocumentFields); 75 | if ($fieldKey !== false) { 76 | unset($unmappedDocumentFields[$fieldKey]); 77 | } 78 | } 79 | 80 | // set up highlights... 81 | $highlight = $this->getResult()->getHighlighting($file->getData('id')); 82 | 83 | if ($highlight && property_exists($highlight,'content')) 84 | $file->setHighlights($highlight->content); 85 | 86 | $dynamicFields = array_diff( 87 | $unmappedDocumentFields, 88 | array_keys(Mage::helper('solr/schema')->getFieldToAttributeMap()) 89 | ); 90 | 91 | // map Solr dynamic fields to Magento product attributes 92 | $dynamicFieldSuffixes = Mage::helper('solr/schema')->getDynamicFieldSuffixes(); 93 | foreach ($dynamicFields as $dynamicField) { 94 | $fieldNameParts = explode('_', $dynamicField); 95 | 96 | // do we have a valid dynamic field? If so, generate attribute name, map to Solr field 97 | if (in_array($fieldNameParts[count($fieldNameParts) - 1], $dynamicFieldSuffixes)) { 98 | array_pop($fieldNameParts); 99 | $attributeName = implode('_', $fieldNameParts); 100 | $file->setData($attributeName, $document->{$dynamicField}); 101 | } 102 | } 103 | 104 | $fileIds[] = $document->fileId; 105 | 106 | $collection->addItem($file); 107 | } 108 | 109 | $collection->addFieldToFilter('file_id', array('in' => $fileIds)); 110 | 111 | $this->setData('collection', $collection); 112 | } 113 | 114 | return $this->getData('collection'); 115 | } 116 | 117 | public function getResultListHtml() 118 | { 119 | /** @var Mage_Core_Block_Template $list */ 120 | $list = $this->getChild('result_list'); 121 | 122 | if (!$list) 123 | return ''; 124 | 125 | $list->setCollection($this->getCollection()); 126 | 127 | return $list->toHtml(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/solr/magentocores/conf/french/schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | id 120 | 121 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 0.12.2 6 | 7 | 8 | 9 | 10 | 11 | 12 | Asm_Solr_Helper 13 | 14 | 15 | 16 | 17 | Asm_Solr_Model 18 | solr_resource 19 | 20 | 21 | Asm_Solr_Model_Resource 22 | 23 | 24 | solr_indexqueue_file
25 |
26 |
27 |
28 | 29 | 30 | Asm_Solr_Model_Cms_Page 31 | 32 | 33 |
34 | 35 | 36 | 37 | solr/indexer_catalog 38 | 39 | 40 | solr/indexer_cms 41 | 42 | 43 | solr/indexer_file 44 | 45 | 46 | 47 | 48 | 49 | string 50 | 51 | 52 | , 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Asm_Solr_Block 61 | 62 | 63 | 64 | 65 | 66 | Asm_Solr 67 | Asm_Solr_Model_Resource_Setup 68 | 69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | Asm_Solr_Adminhtml 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | solr/observer_attribute 91 | addAttributeWeightField 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | solr/observer_product 100 | updateProduct 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | solr/GarbageCollector 109 | deleteProduct 110 | 111 | 112 | 113 | 114 | 115 | 116 | solr/SynonymHandler 117 | updateSynonyms 118 | 119 | 120 | 121 | 122 | 123 | 124 | solr/observer_index_page 125 | trackFiles 126 | 127 | 128 | 129 | 130 | 131 | 132 | solr/GarbageCollector 133 | deleteFile 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | standard 144 | 145 | Asm_Solr 146 | search 147 | 148 | 149 | 150 | 151 | 152 | 153 | solr.xml 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | solr/solr_query_modifier_faceting 162 | modifyQuery 163 | 164 | 165 | solr/solr_query_modifier_sorting 166 | modifyQuery 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | http 177 | localhost 178 | 8080 179 | solr/core_en/ 180 | 181 | 182 | 183 | 184 | pdf, doc, docx 185 | 186 | 187 | 188 | 189 | 0 190 | 0 191 | 0 192 | 0 193 | 194 | 195 | 196 |
-------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Block/Result.php: -------------------------------------------------------------------------------- 1 | _type; 21 | } 22 | 23 | 24 | public function getSolrType() 25 | { 26 | return $this->_solrType; 27 | } 28 | 29 | /** 30 | * @return Asm_Solr_Model_Result 31 | */ 32 | public function getResult() // todo maybe make this an abstract class, or at least put some query prep into the parent 33 | { 34 | // if we don't have a result set for us, let's make one 35 | if (!$this->getData('result')) 36 | { 37 | /** @var Asm_Solr_Model_Result $result */ 38 | $result = Mage::getModel('solr/result'); 39 | 40 | $query = $result->getQuery(); 41 | $query->setKeywords($this->getKeywords()); 42 | $query->addFilter('type', $this->getSolrType()); 43 | $query->addFilter('storeId', $this->getStoreId()); 44 | 45 | $result->load($this->getLimit(), $this->getOffset()); 46 | 47 | $this->setData('result', $result); 48 | } 49 | 50 | return $this->getData('result'); 51 | } 52 | 53 | public function getResultCount() 54 | { 55 | return $this->getResult()->getCount(); 56 | } 57 | 58 | public function getResultDocuments() 59 | { 60 | return $this->getResult()->getDocuments(); 61 | } 62 | 63 | /** 64 | * Prepare layout 65 | * 66 | * @return Asm_Solr_Block_Result 67 | */ 68 | protected function prepareLayout() 69 | { 70 | // add Home breadcrumb 71 | $breadcrumbs = $this->getLayout()->getBlock('breadcrumbs'); 72 | if ($breadcrumbs) { 73 | $title = $this->__("Search results for: '%s'", $this->helper('solr')->getQuery()->getKeywordsCleaned()); 74 | 75 | $breadcrumbs->addCrumb('home', array( 76 | 'label' => $this->__('Home'), 77 | 'title' => $this->__('Go to Home Page'), 78 | 'link' => Mage::getBaseUrl() 79 | ))->addCrumb('search', array( 80 | 'label' => $title, 81 | 'title' => $title 82 | )); 83 | } 84 | 85 | // modify page title 86 | $title = $this->__("Search results for: '%s'", $this->helper('solr')->getQuery()->getKeywordsCleaned()); 87 | $this->getLayout()->getBlock('head')->setTitle($title); 88 | 89 | return parent::_prepareLayout(); 90 | } 91 | 92 | /** 93 | * Retrieve additional blocks html 94 | * 95 | * @return string 96 | */ 97 | public function getAdditionalHtml() 98 | { 99 | return $this->getLayout()->getBlock('search_result_list')->getChildHtml('additional'); 100 | } 101 | 102 | /** 103 | * Retrieve search list toolbar block 104 | * 105 | * @return Mage_Catalog_Block_Product_List 106 | */ 107 | public function getListBlock() 108 | { 109 | return $this->getChild('search_result_list'); 110 | } 111 | 112 | /** 113 | * Set search available list orders 114 | * 115 | * @return Asm_Solr_Block_Result 116 | */ 117 | public function setListOrders() 118 | { 119 | $currentCategory = Mage::getSingleton('catalog/layer') 120 | ->getCurrentCategory(); 121 | /* @var $currentCategory Mage_Catalog_Model_Category */ 122 | $availableOrders = $currentCategory->getAvailableSortByOptions(); 123 | unset($availableOrders['position']); 124 | 125 | $availableOrders = array_merge(array( 126 | 'relevance' => $this->__('Relevance') 127 | ), $availableOrders); 128 | 129 | $this->getListBlock() 130 | ->setAvailableOrders($availableOrders) 131 | ->setDefaultDirection('desc') 132 | ->setSortBy('relevance'); 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * Set Search Result collection 139 | * 140 | * @return Asm_Solr_Block_Result 141 | */ 142 | public function setListCollection() 143 | { 144 | // $this->getListBlock() 145 | // ->setCollection($this->_getProductCollection()); 146 | return $this; 147 | } 148 | 149 | public function getHeaderText() 150 | { 151 | if (!$this->getData('header_text')) 152 | { 153 | // supply a default 154 | $text = $this->__("%s search results for '%s'", uc_words($this->getType()), $this->getKeywordsCleaned()); 155 | 156 | $this->setData('header_text', $text); 157 | } 158 | 159 | return $this->getData('header_text'); 160 | } 161 | /** 162 | * Retrieve No Result or Minimum query length Text 163 | * 164 | * @return string 165 | */ 166 | public function getNoResultText() 167 | { 168 | if (!$this->getData('no_result_text')) 169 | { 170 | // supply a default 171 | $text = $this->__('Your %s search returned no results.', uc_words($this->getType())); 172 | 173 | $this->setData('no_result_text', $text); 174 | } 175 | 176 | return $this->_getData('no_result_text'); 177 | } 178 | 179 | public function getResultListHtml() 180 | { 181 | return $this->getChildHtml('search_result_list'); 182 | } 183 | 184 | } 185 | ?> -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Helper/Data.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class Asm_Solr_Helper_Data extends Mage_Core_Helper_Abstract 11 | { 12 | /** 13 | * Gets the current Solr query 14 | * 15 | * @return Asm_Solr_Model_Solr_Query 16 | */ 17 | public function getQuery() 18 | { 19 | return Mage::getSingleton('solr/solr_query'); 20 | } 21 | 22 | /** 23 | * Gets the current query's result or null if the query has not been 24 | * executed yet. 25 | * 26 | * @return Asm_Solr_Model_Solr_Response|null 27 | */ 28 | public function getResponse() { 29 | return Mage::registry('solr/response'); 30 | } 31 | 32 | /** 33 | * Generates the result page URL 34 | * 35 | * @param string $keywords 36 | * @return string 37 | */ 38 | public function getResultUrl($keywords = null) 39 | { 40 | return $this->_getUrl('search/result', array( 41 | '_query' => array(Asm_Solr_Model_Solr_Query::QUERY_PARAMETER_NAME => $keywords), 42 | '_secure' => Mage::app()->getFrontController()->getRequest()->isSecure() 43 | )); 44 | } 45 | 46 | /** 47 | * Generates the suggest URL 48 | * 49 | * @return string 50 | */ 51 | public function getSuggestUrl() 52 | { 53 | return $this->_getUrl('search/suggest', array( 54 | '_secure' => Mage::app()->getFrontController()->getRequest()->isSecure() 55 | )); 56 | } 57 | 58 | /** 59 | * Generates a document id for documents representing product records. 60 | * 61 | * @param integer $productId Product ID 62 | * @return string The document id for that product 63 | */ 64 | public function getProductDocumentId($productId) 65 | { 66 | $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); 67 | $host = parse_url($baseUrl, PHP_URL_HOST); 68 | $siteHash = $this->getSiteHashForDomain($host); 69 | 70 | $documentId = $siteHash . '/' . Mage_Catalog_Model_Product::ENTITY . '/' . $productId; 71 | 72 | return $documentId; 73 | } 74 | 75 | /** 76 | * Generates a document id for documents representing files. 77 | * 78 | * @param integer $fileId File ID 79 | * @return string The document ID for that file 80 | */ 81 | public function getFileDocumentId($fileId) 82 | { 83 | $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); 84 | $host = parse_url($baseUrl, PHP_URL_HOST); 85 | $siteHash = $this->getSiteHashForDomain($host); 86 | 87 | $documentId = $siteHash . '/' . Asm_Solr_Model_Indexqueue_File::ENTITY . '/' . $fileId; 88 | 89 | return $documentId; 90 | } 91 | 92 | /** 93 | * Generates a document id for documents representing CMS pages. 94 | * 95 | * @param integer $pageId Page ID 96 | * @return string The document id for that page 97 | */ 98 | public function getPageDocumentId($pageId) 99 | { 100 | $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB); 101 | $host = parse_url($baseUrl, PHP_URL_HOST); 102 | $siteHash = $this->getSiteHashForDomain($host); 103 | 104 | $documentId = $siteHash . '/' . Asm_Solr_Model_Cms_Page::ENTITY . '/' . $pageId; 105 | 106 | return $documentId; 107 | } 108 | 109 | /** 110 | * Gets the site hash for a domain 111 | * 112 | * @param string $domain Domain to calculate the site hash for. 113 | * @return string site hash for $domain 114 | */ 115 | public function getSiteHashForDomain($domain) 116 | { 117 | $encryptionKey = Mage::getStoreConfig('global/crypt/key'); 118 | 119 | $siteHash = sha1( 120 | $domain . 121 | $encryptionKey . 122 | 'Asm_Solr' 123 | ); 124 | 125 | return $siteHash; 126 | } 127 | 128 | /** 129 | * Gets an instance of the logger 130 | * 131 | * @return Asm_Solr_Helper_Logger 132 | */ 133 | public function getLogger() 134 | { 135 | return Mage::helper('solr/logger'); 136 | } 137 | 138 | /** 139 | * Takes a Magento date string or Zend_Date and turns it into an 140 | * ISO 8601 compliant formatted string of the date. 141 | * 142 | * @param string|integer|Zend_Date $date Magento date string, Unix timestamp, or Zend_Date object 143 | * @return string ISO date (using Z instead of +00:00) 144 | */ 145 | public function dateToIso($date) 146 | { 147 | if (is_string($date)) { 148 | $date = new Zend_Date($date); 149 | } 150 | 151 | if (is_int($date)) { 152 | $date = new Zend_Date($date, Zend_Date::TIMESTAMP); 153 | } 154 | 155 | return str_replace('+00:00', 'Z', $date->getIso()); 156 | } 157 | 158 | /** 159 | * Explodes a string and trims all values for whitespace in the ends. 160 | * If $onlyNonEmptyValues is set, then all blank ('') values are removed. 161 | * 162 | * @param string $delimiter Delimiter string to explode with 163 | * @param string $string The string to explode 164 | * @param boolean $removeEmptyValues If set, all empty values will be removed from output 165 | * @return array Exploded values 166 | */ 167 | public function trimExplode($delimiter, $string, $removeEmptyValues = false) 168 | { 169 | $explodedValues = explode($delimiter, $string); 170 | $result = array_map('trim', $explodedValues); 171 | 172 | if ($removeEmptyValues) { 173 | $temp = array(); 174 | foreach ($result as $value) { 175 | if ($value !== '') { 176 | $temp[] = $value; 177 | } 178 | } 179 | $result = $temp; 180 | } 181 | 182 | return $result; 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Model/Indexqueue/File.php: -------------------------------------------------------------------------------- 1 | 25 | * 26 | * @method string getFilePath() 27 | * @method integer getCmsPageId() 28 | * @method integer getStoreId() 29 | * @method integer getIndexed() 30 | * @method integer getChanged() 31 | */ 32 | class Asm_Solr_Model_Indexqueue_File extends Mage_Core_Model_Abstract { 33 | 34 | const ENTITY = 'indexqueue_file'; 35 | 36 | /** 37 | * File extensions supported to extract text from. 38 | * 39 | * Since we're using Tika, this list is actually a lot longer but this will 40 | * keep life easy for now. 41 | * 42 | * @var array 43 | */ 44 | protected $supportedTextFileExtensions = array('pdf', 'doc', 'docx', 'txt', 'odf'); 45 | 46 | /** 47 | * Prefix of model events names 48 | * 49 | * @var string 50 | */ 51 | protected $_eventPrefix = self::ENTITY; 52 | 53 | /** 54 | * Parameter name in event 55 | * 56 | * In observe method you can use $observer->getEvent()->getIndexqueueFile() in this case 57 | * 58 | * @var string 59 | */ 60 | protected $_eventObject = self::ENTITY; 61 | 62 | /** 63 | * Internal cache in case the content is requested multiple times 64 | * 65 | * @var string 66 | */ 67 | private $content = null; 68 | 69 | 70 | protected function _construct() 71 | { 72 | $this->_init('solr/indexqueue_file'); 73 | } 74 | 75 | /** 76 | * Tries to return the file's content if it's a text file or the file's 77 | * textual representation otherwise. 78 | * 79 | * @return string The file's text content 80 | */ 81 | public function getContent() 82 | { 83 | if (is_null($this->content)) { 84 | $fileContent = ''; 85 | $mimeType = $this->getMimeType(); 86 | 87 | if ($mimeType == 'text/plain') { 88 | // we can read text files directly 89 | $fileContent = file_get_contents($this->getFileAbsolutePath()); 90 | } else if (in_array($this->getExtension(), $this->supportedTextFileExtensions)) { 91 | $fileContent = $this->extractContent(); 92 | } else { 93 | $fileContent = ''; 94 | } 95 | 96 | $this->content = Mage::helper('solr/contentExtractor')->cleanContent($fileContent); 97 | } 98 | 99 | return $this->content; 100 | } 101 | 102 | /** 103 | * Extracts textual content from a file using Solr's extract response handler 104 | * 105 | * @return string The file's text content 106 | * @throws Apache_Solr_InvalidArgumentException 107 | */ 108 | protected function extractContent() 109 | { 110 | $solr = Mage::helper('solr/connectionManager')->getConnection(); 111 | $response = $solr->extract($this->getFileAbsolutePath(), array( 112 | 'resource.name' => 'file_content', 113 | 'extractOnly' => 'true', 114 | 'extractFormat' => 'text' 115 | )); 116 | 117 | $content = $response->_empty_; 118 | //$metadata = (array) $response->null_metadata; 119 | 120 | return $content; 121 | } 122 | 123 | /** 124 | * Determines the Internet Media Type, or MIME type. 125 | * 126 | * @return string The file's MIME type. 127 | */ 128 | public function getMimeType() 129 | { 130 | $mimeType = ''; 131 | 132 | if (function_exists('finfo_file')) { 133 | $fileInfo = new finfo(FILEINFO_MIME_TYPE); 134 | if ($fileInfo) { 135 | $mimeType = $fileInfo->file($this->getFileAbsolutePath()); 136 | } 137 | } else { 138 | $mimeType = mime_content_type($this->getFileAbsolutePath()); 139 | } 140 | 141 | return $mimeType; 142 | } 143 | 144 | /** 145 | * Gets the file's basename 146 | * 147 | * @return string 148 | */ 149 | public function getName() 150 | { 151 | return basename($this->getFilePath()); 152 | } 153 | 154 | /** 155 | * Gets the file's extension 156 | * 157 | * @return string 158 | */ 159 | public function getExtension() 160 | { 161 | $filePath = $this->getFilePath(); 162 | $fileName = basename($filePath); 163 | 164 | $fileNameParts = explode('.', $fileName); 165 | 166 | return array_pop($fileNameParts); 167 | } 168 | 169 | public function getUrl() 170 | { 171 | return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB) . substr($this->getFilePath(), 1); 172 | } 173 | 174 | /** 175 | * Gets the file's absolute path 176 | * 177 | * @return string 178 | */ 179 | public function getFileAbsolutePath() 180 | { 181 | return Mage::getBaseDir('base') . $this->getFilePath(); 182 | } 183 | 184 | /** 185 | * Gets the (unix)time the file was last modified. 186 | * 187 | * @return integer Unix timestamp of the last modification of the file 188 | */ 189 | public function getFileLastChangedTime() 190 | { 191 | return filemtime($this->getFileAbsolutePath()); 192 | } 193 | 194 | } -------------------------------------------------------------------------------- /app/code/community/Asm/Solr/Resources/tomcat/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 50 | 54 | 55 | 61 | 65 | 70 | 71 | 72 | 75 | 76 | 77 | 82 | 83 | 86 | 87 | 88 | 91 | 94 | 95 | 97 | 98 | 102 | 104 | 105 | 106 | 108 | 109 | 111 | 114 | 115 | 118 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /app/design/frontend/base/default/template/solr/facets.phtml: -------------------------------------------------------------------------------- 1 | getResult()->getResponse(); 3 | $_facet_ranges = $this->getFacetRanges(); 4 | $gap = $_facet_ranges->price->gap - 0.01; 5 | $start = $_facet_ranges->price->start; 6 | $end = $_facet_ranges->price->end; 7 | $facet_ranges_results = $_facet_ranges->price->counts; 8 | 9 | $_facet_fields = $this->getFacetFields(); 10 | $category_field_results = $_facet_fields->categoryId; 11 | $functions_field_results = $_facet_fields->tcm_functions_stringM; 12 | $all_category = $this->getAllCategories($category_field_results); 13 | 14 | $current_filters_applied = $this->getCurrentFilters(); 15 | 16 | $test='bob'; 17 | //$_price = $_facet_ranges->price->counts->{"0.0"}; 18 | ?> 19 |
20 |
21 | __('Narrow Search') ?> 22 |
23 |
24 | 25 | getAppliedFiltersHtml() ?> 26 | 27 |

__('Filter Options') ?>

28 | 29 |
30 |
31 |
32 |
    33 |

    Filters Applied

    34 | $result): ?> 35 |
  1. 36 | Remove This Item 41 | 43 |
  2. 44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 |
    54 |

    Price

    55 | $result): ?> 56 |
  1. 57 | 65 | 66 | 69 | 70 | 71 | 72 | 73 |
  2. 74 | 75 |
76 |
77 | 78 |
79 |
80 |
81 |
82 |
83 |
    84 |

    Category

    85 | $result): ?> 86 |
  1. 87 | 93 | 94 | 97 |
    98 | 99 |
    100 | 101 |
  2. 102 | 103 |
104 |
105 | 106 |
107 |
108 |
109 |
110 |
111 |
    112 |

    Functions

    113 | $result): ?> 114 |
  1. 115 | 122 | "> 123 | 126 |
    127 | 128 |
    129 | 130 |
  2. 131 | 132 |
133 |
134 | 135 |
136 | 137 |
138 |
139 | --------------------------------------------------------------------------------