Xavier Briand @xavierbriand
Senior web developer at experiencePoint
Symfony2
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget dolor mattis eros gravida commodo. Donec fermentum lacinia est vel laoreet. Nunc varius felis at libero volutpat imperdiet. Cras at elit purus. Fusce sit amet accumsan mauris. Fusce in consequat odio. Nunc eget sem porttitor nibh commodo luctus. Vivamus fringilla scelerisque massa vel auctor. Integer sollicitudin, augue eu laoreet viverra, erat leo luctus elit, eu tristique nulla mauris sit amet mi. Quisque convallis ante at lorem vehicula semper ornare velit pulvinar. Nulla vulputate arcu feugiat leo eleifend et ullamcorper arcu cursus. Aenean suscipit rutrum tortor, ac rhoncus massa iaculis at. Sed imperdiet, augue at fermentum viverra, velit justo tincidunt nulla, a sagittis arcu nisl vitae ipsum. Proin congue diam sed elit pellentesque tristique. Nunc sagittis, massa vitae accumsan ornare, dui ipsum hendrerit neque, at faucibus nisi turpis a arcu. Proin commodo elementum tellus vel semper. Maecenas porttitor, velit nec sodales vestibulum, nisl lorem imperdiet arcu, quis fermentum sem lorem at mi. Nam ultrices mauris sed dolor dignissim hendrerit. Donec sit amet tristique tellus. Etiam ac sem sapien. Pellentesque rutrum ligula id turpis elementum dapibus non ac nisl. Integer hendrerit dictum viverra. Donec sit amet fringilla dui. Quisque ligula dolor, tristique ut mollis vel, tincidunt a enim. Nulla id ante ipsum. Cras felis turpis, ultrices non pharetra ac, euismod ut justo. Nunc aliquet tincidunt rutrum. Morbi vel nisl nulla. Integer dignissim posuere lectus vel tincidunt. Morbi pellentesque ipsum at orci pretium ut porttitor nisi tempor. Sed leo lectus, mollis vitae porttitor posuere, convallis eu lacus. Phasellus sodales sagittis eros vel ornare. Sed malesuada tincidunt felis eget consequat. Morbi sem massa, interdum quis iaculis in, volutpat sed odio. Nulla facilisi. Morbi mattis interdum viverra. Praesent aliquet feugiat massa, sit amet condimentum est accumsan vel. Sed pharetra ornare nisl, sed blandit est viverra quis. Aliquam a tortor neque, et porta velit. Curabitur mattis metus nec justo venenatis vel viverra odio faucibus. Mauris libero neque, consectetur a molestie ut, auctor id dui. Sed dapibus lacus vitae erat convallis dignissim. Mauris molestie convallis sem, sit amet elementum orci interdum vitae. Vivamus et dictum neque. Sed vitae purus justo, quis ullamcorper nunc. Donec nec erat diam, eu sagittis lorem. Fusce interdum semper euismod. Duis lacus purus, faucibus vel facilisis at, scelerisque vitae felis. Aliquam sapien diam, commodo in ullamcorper at, adipiscing vitae lorem. Fusce blandit massa non lacus volutpat quis tincidunt dui convallis. Ut tincidunt lorem eget tortor eleifend varius. In hac habitasse platea dictumst. Donec suscipit pellentesque lectus vel aliquam. Suspendisse fringilla dignissim felis id tincidunt. Mauris volutpat, diam eget faucibus vestibulum, dui leo sodales velit, eu mollis magna lectus eget risus. Fusce hendrerit porttitor felis, nec condimentum lectus lacinia id. In convallis nisi eget enim vehicula vitae vehicula neque suscipit. Suspendisse condimentum felis a tortor bibendum placerat. Proin at justo ac sem tempus aliquet eget non lectus. Maecenas hendrerit arcu in lacus tincidunt volutpat. Mauris et purus sit amet arcu auctor dapibus eu sit amet nibh. Pellentesque dignissim leo metus, ac tincidunt dui. Nulla facilisi. Suspendisse potenti. In fermentum quam eu libero molestie malesuada. Aenean dui mauris, consequat id rutrum in, iaculis a arcu. In in nisl nisl. Sed nec magna at metus luctus eleifend et quis neque. Sed luctus ullamcorper luctus. Sed sit amet eros ac odio consectetur pretium sodales a augue. Suspendisse euismod, nulla sed cursus lobortis, ligula erat interdum enim, blandit laoreet felis tortor sed sem. In tincidunt tellus et neque scelerisque congue. Donec lorem sem, tempus nec varius sit amet, porttitor at orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eget dolor mattis eros gravida commodo. Donec fermentum lacinia est vel laoreet. Nunc varius felis at libero volutpat imperdiet. Cras at elit purus. Fusce sit amet accumsan mauris. Fusce in consequat odio. Nunc eget sem porttitor nibh commodo luctus. Vivamus fringilla scelerisque massa vel auctor. Integer sollicitudin, augue eu laoreet viverra, erat leo luctus elit, eu tristique nulla mauris sit amet mi. Quisque convallis ante at lorem vehicula semper ornare velit pulvinar. Nulla vulputate arcu feugiat leo eleifend et ullamcorper arcu cursus. Aenean suscipit rutrum tortor, ac rhoncus massa iaculis at. Sed imperdiet, augue at fermentum viverra, velit justo tincidunt nulla, a sagittis arcu nisl vitae ipsum. Proin congue diam sed elit pellentesque tristique. Nunc sagittis, massa vitae accumsan ornare, dui ipsum hendrerit neque, at faucibus nisi turpis a arcu. Proin commodo elementum tellus vel semper. Maecenas porttitor, velit nec sodales vestibulum, nisl lorem imperdiet arcu, quis fermentum sem lorem at mi. Nam ultrices mauris sed dolor dignissim hendrerit. Donec sit amet tristique tellus. Etiam ac sem sapien. Pellentesque rutrum ligula id turpis elementum dapibus non ac nisl. Integer hendrerit dictum viverra. Donec sit amet fringilla dui. Quisque ligula dolor, tristique ut mollis vel, tincidunt a enim. Nulla id ante ipsum. Cras felis turpis, ultrices non pharetra ac, euismod ut justo. Nunc aliquet tincidunt rutrum. Morbi vel nisl nulla. Integer dignissim posuere lectus vel tincidunt. Morbi pellentesque ipsum at orci pretium ut porttitor nisi tempor. Sed leo lectus, mollis vitae porttitor posuere, convallis eu lacus. Phasellus sodales sagittis eros vel ornare. Sed malesuada tincidunt felis eget consequat. Morbi sem massa, interdum quis iaculis in, volutpat sed odio. Nulla facilisi. Morbi mattis interdum viverra. Praesent aliquet feugiat massa, sit amet condimentum est accumsan vel. Sed pharetra ornare nisl, sed blandit est viverra quis. Aliquam a tortor neque, et porta velit. Curabitur mattis metus nec justo venenatis vel viverra odio faucibus. Mauris libero neque, consectetur a molestie ut, auctor id dui. Sed dapibus lacus vitae erat convallis dignissim. Mauris molestie convallis sem, sit amet elementum orci interdum vitae. Vivamus et dictum neque. Sed vitae purus justo, quis ullamcorper nunc. Donec nec erat diam, eu sagittis lorem. Fusce interdum semper euismod. Duis lacus purus, faucibus vel facilisis at, scelerisque vitae felis. Aliquam sapien diam, commodo in ullamcorper at, adipiscing vitae lorem. Fusce blandit massa non lacus volutpat quis tincidunt dui convallis. Ut tincidunt lorem eget tortor eleifend varius. In hac habitasse platea dictumst. Donec suscipit pellentesque lectus vel aliquam. Suspendisse fringilla dignissim felis id tincidunt. Mauris volutpat, diam eget faucibus vestibulum, dui leo sodales velit, eu mollis magna lectus eget risus. Fusce hendrerit porttitor felis, nec condimentum lectus lacinia id. In convallis nisi eget enim vehicula vitae vehicula neque suscipit. Suspendisse condimentum felis a tortor bibendum placerat. Proin at justo ac sem tempus aliquet eget non lectus. Maecenas hendrerit arcu in lacus tincidunt volutpat. Mauris et purus sit amet arcu auctor dapibus eu sit amet nibh. Pellentesque dignissim leo metus, ac tincidunt dui. Nulla facilisi. Suspendisse potenti. In fermentum quam eu libero molestie malesuada. Aenean dui mauris, consequat id rutrum in, iaculis a arcu. In in nisl nisl. Sed nec magna at metus luctus eleifend et quis neque. Sed luctus ullamcorper luctus. Sed sit amet eros ac odio consectetur pretium sodales a augue. Suspendisse euismod, nulla sed cursus lobortis, ligula erat interdum enim, blandit laoreet felis tortor sed sem. In tincidunt tellus et neque scelerisque congue. Donec lorem sem, tempus nec varius sit amet, porttitor at orci.
Challenge accepted!
Question?
How to
easily&
efficiently
?
laplateforme.com
symfony.com
connect.sensiolabs.com
Solr
Solr is a standalone enterprise search server with a REST-like API. You put documents in it [...] over HTTP. You query it via HTTP GET and receive [...] results.
Full-Text Search High Volume Standards Admin Scalable Flexible Replication Adaptable Extensible

Schema

Query

Core

Caching

Replication

Admin Interface

A lot!
Lucene
Twitter, Apple, wikipedia, LinkedIn, ...
Solr
Etsy, AOL yellow pages, Sears, SourceForge, Nasa, digg, buy.com, OverBlog, ...
$ wget http://(...)/3.6.0/apache-solr-3.6.0.tgz

$ tar -xfz apache-solr-3.6.0.tgz

$ cd apache-solr-3.6.0/example/

$ java -jar start.jar

(a lot of java log...)
http://localhost:8983/solr/admin/
Administration Solr
http://localhost:8983/solr/browse
Solaritas
We need a bridge...
http://wiki.apache.org/solr/SolPHP

Solarium

http://www.solarium-project.org/
Solarium
There is a Bundle for that!
github
with
Indexing
One Index
One schema
n fields
./config/schema.xml
<fields>
  <field name="id" type="string" indexed="true" stored="true" required="true" />
  <field name="description" type="text_general" indexed="true" stored="true"/>
  <field name="path" type="string" indexed="false" stored="true"/>
</fields>
<types>
  <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"  />
      <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" />
      <filter class="solr.LowerCaseFilterFactory"/>
      <filter class="solr.PorterStemFilterFactory"/>
    </analyzer>
  </fieldType>
  <!-- ... -->
</types>
I'm looking for Wi-Fi
I | m | looking | for | Wi-Fi
looking | Wi-fi
looking | WiFi
looking | wifi
look | wifi
JaMes looks aT the wIfi signal
JaMes | looks | aT | the | wIfi | signal
JaMes | looks | wIfi | signal
JaMes | looks | wIfi | signal
james | looks | wifi | signal
james | look | wifi | signal
// Acme/BlogBundle/Entity/BlogPost.php
class BlogPost
{
    //...
    public function toSolrDocument(\Solarium_Document_ReadWrite $doc)
    {
        $doc->id                  = $this->getId();
        $doc->description = $this->getTitle().' '.$this->getBody();
        $doc->path             = $this->getSlug();

        return $doc;
    }
    //...
}
// Acme/BlogBundle/Command/SolrIndexCommand.php
$client = $this->getContainer()->get('solarium');

// get an update query instance
$update = $client->createUpdate();

// create documents
$documents = array();
foreach ($blogPosts as $blogPost) {
    $documents[] = $blogPost->toSolrDocument($update->createDocument());
}

// add the documents and a commit command to the update query
$update->addDocuments($documents);
$update->addCommit();

// this executes the query and returns the result
$result = $client->update($update);
$client = $this->get('solarium');

$query = $client->createSelect();

$query->setQuery('description:"Foo Bar"');

$query->setStart(1)->setRows(10);

$resultset = $client->select($query);
<ul>
{% for doc in resultset %}
  <li> {{ doc.description }} </li>
{% endfor %}
</ul>
Relevency
TF - IDF
Coord
lengthNorm
Boost
Title > Text
 <field name="title" type="text_general" indexed="true" stored="true"/>
$query->setQuery('"Foo Bar"');

$query->getDisMax()->setQueryParser('edismax');

$query->getDisMax()->setQueryFields('title^2 description');
Über feature
Faceting
faceting
$query = $client->createSelect();

$facetSet = $query->getFacetSet();

$facetSet->createFacetField('cat')->setField('category');

{% for facet, value in resultset.facetSet.facet('category') %}
  {{ facet }} ({{ value }})
{% endfor %}
Filtering
X
$query->setQuery('Chocolat category:book');
$query->setQuery('Chocolat');

$query->createFilterQuery('cat')->setQuery('category:book');
Contrast ◑
highlight
highlight
$query->getHighlighting()->setFields('description');
$query->getHighlighting()->setSimplePrefix('<em>');
$query->getHighlighting()->setSimplePostfix('</em>');

<ul>
{% for doc in resultset %}
  <li>
    {{ doc.title }}<br />
    {% for text in resultset.highlighting.result(doc.id).field('description') %}
      {{ text|raw }}
    {% endfor %}
  </li>
{% endfor %}
</ul>
Can't find resluts!
spellchecker
<!-- request handler ./config/solrconfig.xml -->
    <!-- ... -->
    <arr name="last-components">
        <str>spellcheck</str>
    </arr>
</requestHandler>
$query->getSpellcheck()->setQuery('misplelign');
$query->getSpellcheck()->setCollateExtendedResults(true);
{% if resultset.spellcheck.correctlySpelled %}

{% for suggestion in resultset.spellcheck %}
    {{ suggestion.word }}
{% endfor %}

{% for collation in resultset.spellcheck.collation.corrections %}
    {{ collation }}
{% endfor %}
Über feature again!
Auto..
Autopsy
Automatic
Autosuggest
Automobile
???
Serv...
=
Service Con...
=
Service Container
<!-- ./config/schema.xml -->
<fieldType name="suggester" class="solr.TextField">
    <analyzer type="index">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
        <filter class="solr.EdgeNGramFilterFactory" minGramSize="3"
                        maxGramSize="15" side="front"/>
    </analyzer>
    <analyzer type="query">
        <tokenizer class="solr.KeywordTokenizerFactory"/>
    </analyzer>
</fieldType>
Ser
Serv
Servi
Servic
Service
Service
Service C
Service Co
...
jqueryui.com/demos/autocomplete/#remote
Je ne parle pas français
/
I don't speak english
multicore
=
multi-index (schema.xml)
=
multi-config (solrconfig.xml)
<!-- ./solr.xml -->
<!-- from -->
<solr>
    <cores adminPath="/admin/cores">
        <core name="collection1" instanceDir="." />
    </cores>
</solr>
<!-- to -->
<solr>
    <cores adminPath="/admin/cores">
        <core name="english" instanceDir="core_en/"/>
        <core name="french" instanceDir="core_fr/"/>
    </cores>
</solr>
▾ solr/
  ▾ conf/
      schema.xml
      solrconfig.xml
  ▸ data/
  README.txt
  solr.xml
▾ solr/
  ▾ core_en/
    ▾ conf/
        schema.xml
        solrconfig.xml
    ▸ data/
  ▾ core_fr/
    ▾ conf/
        schema.xml
        solrconfig.xml
    ▸ data/
    README.txt
    solr.xml
$client->getAdapter()->setCore('french');
http://localhost:8983/solr/admin/cores?action=UNLOAD&core=collection1
move / duplicate / setup config
http://local:8983/solr/admin/cores?action=CREATE&name=english&instanceDir=core_en/
http://local:8983/solr/admin/cores?action=CREATE&name=french&instanceDir=core_fr/
STATUS / RELOAD / RENAME / ALIAS / SWAP
Multicore
=
replication load balancing
sharding

Unlimited Power
SUM UP
$client = $this->get('solarium');
$query = $client->createSelect();
$query->setQuery('text:"Foo Bar"');
$query->setStart(1)->setRows(10);
$query->getDisMax()->setQueryParser('edismax');
$query->getDisMax()->setQueryFields('title^2 description');
$query->getFacetSet()->createFacetField('cat')->setField('category');
// + query filtering
$query->getHighlighting()->setFields('description');
$query->getHighlighting()->setSimplePrefix('<em>');
$query->getHighlighting()->setSimplePostfix('</em>');
$query->getSpellcheck()->setQuery('misplelign');
$query->getSpellcheck()->setCollateExtendedResults(true);
$resultset = $client->select($query);
Solarium Plugin
abstract class Solarium_Plugin_Abstract
{
    protected function _initPlugin()
    public function preCreateRequest($query)
    public function postCreateRequest($query, $request)
    public function preExecuteRequest($request)
    public function postExecuteRequest($request, $response)
    public function preCreateResult($query, $response)
    public function postCreateResult($query, $response, $result)
    public function preExecute($query)
    public function postExecute($query, $result)
    public function preCreateQuery($type, $options)
    public function postCreateQuery($type, $options, $query)
}
Use the DIC, Luke.
<service id="solarium.autosuggest" parent="solarium">
    <call method="registerPlugin">
        <argument>autosuggest</argument>
        <argument type="service" id="solarium.plugin.autosuggest" />
    </call>
</service>
<service id="solarium.plugin.autosuggest" class="%class%" public="false" >
    <argument type="service" id="router" />
</service>
// Sensio\Bundle\SearchBundle\Solarium\Plugin\Autosuggest
class Autosuggest extends \Solarium_Plugin_Abstract
{
    public function __construct(Router $router)

    protected function _initPlugin()
    {
        $this->_client->registerQueryType(
            \Solarium_Client::QUERYTYPE_SELECT,
            'Sensio\Bundle\SearchBundle\Solarium\Query\Autosuggest',
            'Solarium_Client_RequestBuilder_Select',
            'Solarium_Client_ResponseParser_Select'
        );
    }

    public function preCreateResult($query, $response)
    {
        // generate routes for your jquery autosuggest response
    }
    // (...)
// Sensio\Bundle\SearchBundle\Solarium\Query\Autosuggest
class Autosuggest extends \Solarium_Query_Select
{
    protected function _init()
    {
        $this->createFilterQuery('enabled')->setQuery('is_active:true');
        $this->getDisMax()->setQueryFields('autosuggest');
        $this->getGrouping()->addField('type');
        $this->setFields('id, name, link');
    }
}
▾ Solarium/
    ▾ Plugin/
        Autosuggest.php
        Search.php
    ▾ Query/
        Autosuggest.php
        Search.php
Request service
=
serch terms, page, filter values, ....
Question?