<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rafał Andrzejewski &#8211; Solr.pl</title>
	<atom:link href="https://solr.pl/author/randrzjewski/feed/" rel="self" type="application/rss+xml" />
	<link>https://solr.pl</link>
	<description>All things to be found - Blog related to Apache Solr &#38; Lucene projects - https://solr.apache.org</description>
	<lastBuildDate>Wed, 11 Nov 2020 20:28:46 +0000</lastBuildDate>
	<language>pl-PL</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
	<item>
		<title>Aplikacja „sprzedaż samochodów” – solr.ReversedWildcardFilter, czyli optymalizujemy zapytania wildcard (cz. 8)</title>
		<link>https://solr.pl/2011/10/10/aplikacja-sprzedaz-samochodow-solr-reversedwildcardfilter-czyli-optymalizujemy-zapytania-wildcard-cz-8/</link>
					<comments>https://solr.pl/2011/10/10/aplikacja-sprzedaz-samochodow-solr-reversedwildcardfilter-czyli-optymalizujemy-zapytania-wildcard-cz-8/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 10 Oct 2011 19:28:21 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=323</guid>

					<description><![CDATA[Użytkownicy aplikacji &#8222;sprzedaż samochodów&#8221; zaczęli coraz częściej używać zapytań ze znakami wildcard. Zmusiło nas to do zastanowienia się nad optymalizacją takich zapytań, a na pomoc przyszedł filtr solr.ReversedWildcardFilter. solr.ReversedWildcardFilter Filtr solr.ReversedWildcardFilter dostarcza nam do indeksu nowe tokeny, które są niejako]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">Użytkownicy aplikacji &#8222;sprzedaż samochodów&#8221; zaczęli coraz częściej używać zapytań ze znakami wildcard. Zmusiło nas to do zastanowienia się nad optymalizacją takich zapytań, a na pomoc przyszedł filtr solr.ReversedWildcardFilter.</p>
<p><span id="more-323"></span></p>
<h3>solr.ReversedWildcardFilter</h3>
<p lang="pl-PL">Filtr solr.ReversedWildcardFilter dostarcza nam do indeksu nowe tokeny, które są niejako odwróceniem tokenów w danym polu. Tokeny takie są wykorzystywane podczas wyszukiwania, w celu przyspieszenia zadawania zapytań, w których &#8222;dzikie karty&#8221; są na początku wyrażenia. Na poziomie konfiguracji filtr dostarcza nam następujące argumenty:</p>
<ul>
<li><em>withOriginal</em> &#8211; jeżeli &#8222;true&#8221;, to generuj na tej samej pozycji zarówno tokeny oryginalne, jak i odwrócone. Jeżeli &#8222;false&#8221;, to generuj jedynie tokeny odwrócone.</li>
<li><em>maxPosAsterisk</em> &#8211; maksymalna pozycja &#8222;dzikiej karty&#8221; &#8222;*&#8221;, która uaktywnia korzystanie przez filtr z tokenów odwróconych. Jeżeli &#8222;*&#8221; pojawi się w zapytaniu na pozycji wyższej niż skonfigurowana, wtedy filtr nie będzie korzystał z tokenów odwróconych.</li>
<li><em>maxPosQuestion</em> &#8211; maksymalna pozycja &#8222;dzikiej karty&#8221; &#8222;?&#8221;, która uaktywnia korzystanie przez filtr z tokenów odwróconych.</li>
<li><em>maxFractionAsterisk</em> &#8211; dodatkowy parametr, który uaktywnia korzystanie z tokenów odwróconych, jeżeli pozycja &#8222;*&#8221; jest mniejsza niż skonfigurowana w tym parametrze wartość ułamkowa długości tokena zapytania.</li>
<li><em>minTrailing</em> &#8211; minimalna liczba znaków występujących po ostatniej &#8222;dzikiej karcie&#8221; w zapytaniu, która uaktywni nam korzystanie z tokenów odwróconych. W celach wydajnościowych zaleca się, aby wartość ta była większa niż 1.</li>
</ul>
<h3>Zmiany w schema.xml</h3>
<p lang="pl-PL">Nowy filtr dodajemy do definicji typu „text” w następujący sposób:</p>
<pre class="brush:xml">&lt;fieldType name="text" class="solr.TextField"
	positionIncrementGap="100"&gt;
	&lt;analyzer type="index"&gt;
		&lt;tokenizer class="solr.WhitespaceTokenizerFactory" /&gt;
		&lt;filter class="solr.PatternReplaceFilterFactory" pattern="'"
			replacement="" replace="all" /&gt;
		&lt;filter class="solr.WordDelimiterFilterFactory"
			generateWordParts="1" generateNumberParts="1" catenateWords="1"
			stemEnglishPossessive="0" /&gt;
		&lt;filter class="solr.LowerCaseFilterFactory" /&gt;
		<strong>&lt;filter class="solr.ReversedWildcardFilterFactory" /&gt;</strong>
	&lt;/analyzer&gt;
	&lt;analyzer type="query"&gt;
		&lt;tokenizer class="solr.WhitespaceTokenizerFactory" /&gt;
		&lt;filter class="solr.PatternReplaceFilterFactory" pattern="'"
			replacement="" replace="all" /&gt;
		&lt;filter class="solr.WordDelimiterFilterFactory"
			generateWordParts="1" generateNumberParts="1" catenateWords="1"
			stemEnglishPossessive="0" /&gt;
		&lt;filter class="solr.LowerCaseFilterFactory" /&gt;
	&lt;/analyzer&gt;
&lt;/fieldType&gt;</pre>
<p lang="pl-PL">Filtr solr.ReversedWildcardFilter wykorzystujemy tylko na etapie indeksacji. Nie definiujemy żadnych argumentów w filtrze, ponieważ chcemy skorzystać ze standardowej konfiguracji, czyli wartości argumentów tego filtra domyślnie będą wyglądać tak:</p>
<ul>
<li><em>withOriginal</em> &#8211; &#8222;true&#8221;, chcemy również mieć dostępne tokeny oryginalne, aby móc dalej korzystać z wyszukiwania bez &#8222;dzikich kart&#8221; po polach tego typu</li>
<li><em>maxPosAsterisk</em> &#8211; 2</li>
<li><em>maxPosQuestion</em> &#8211; 1</li>
<li><em>maxPosQuestion</em> &#8211; 0.0f (czyli funkcjonalność wyłączona)</li>
<li><em>maxPosQuestion</em> &#8211; 2</li>
</ul>
<h3>Przykładowe dane</h3>
<p lang="pl-PL">Zaindeksujmy przykładowe dane do analizy:</p>
<pre class="brush:xml">&lt;add&gt;
  &lt;doc&gt;
    &lt;field name="id"&gt;1&lt;/field&gt;
    &lt;field name="make"&gt;Lancia&lt;/field&gt;
    &lt;field name="model"&gt;Delta&lt;/field&gt;
    ...
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;field name="id"&gt;2&lt;/field&gt;
    &lt;field name="make"&gt;Land Rover&lt;/field&gt;
    &lt;field name="model"&gt;Defender&lt;/field&gt;
    ...
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;field name="id"&gt;3&lt;/field&gt;
    &lt;field name="make"&gt;Acura&lt;/field&gt;
    &lt;field name="model"&gt;MDX&lt;/field&gt;
    ...
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;field name="id"&gt;4&lt;/field&gt;
    &lt;field name="make"&gt;Acura&lt;/field&gt;
    &lt;field name="model"&gt;RDX&lt;/field&gt;
    ...
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;field name="id"&gt;5&lt;/field&gt;
    &lt;field name="make"&gt;Acura&lt;/field&gt;
    &lt;field name="model"&gt;RSX&lt;/field&gt;
    ...
  &lt;/doc&gt;
&lt;/add&gt;</pre>
<h3>Tworzymy zapytania</h3>
<p lang="pl-PL">Przypomnijmy, że domyślne wyszukiwanie odbywa się po polu „content”, w skład którego wchodzą między innymi pola „make” oraz „model”. W celach analizy wyników i działania filtra solr.ReversedWildcardFilter, ustawimy atrybut „stored” pola „content” na „true”. Dodamy również do zapytania argument debugQuery, który umożliwi nam obserwacje, z którego tokena (oryginalnego, czy odwróconego) korzysta filtr.</p>
<ol>
<li>?q=lan*&amp;fl=id,content&amp;debugQuery=on
<pre class="brush:xml">&lt;result name="response" numFound="2" start="0"&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Lancia&lt;/str&gt;
      &lt;str&gt;Delta&lt;/str&gt;
      &lt;str&gt;2002&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;1&lt;/str&gt;
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Land Rover&lt;/str&gt;
      &lt;str&gt;Defender&lt;/str&gt;
      &lt;str&gt;2002&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;2&lt;/str&gt;
  &lt;/doc&gt;
&lt;/result&gt;
&lt;lst name="debug"&gt;
  &lt;str name="rawquerystring"&gt;lan*&lt;/str&gt;
  &lt;str name="querystring"&gt;lan*&lt;/str&gt;
  &lt;str name="parsedquery"&gt;content:lan*&lt;/str&gt;
  &lt;str name="parsedquery_toString"&gt;content:lan*&lt;/str&gt;
  ...
&lt;/lst&gt;</pre>
<p lang="pl-PL">Użyliśmy „dzikiej karty” „*” na końcu zapytania (pozycja = 4), zatem filtr do wyszukiwania użył tokenów oryginalnych:</p>
<pre class="brush:xml">&lt;str name="parsedquery"&gt;content:lan*&lt;/str&gt;</pre>
</li>
<li>?q=*dx&amp;fl=id,content&amp;debugQuery=on
<pre class="brush:xml">&lt;result name="response" numFound="2" start="0"&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Acura&lt;/str&gt;
      &lt;str&gt;MDX&lt;/str&gt;
      &lt;str&gt;2002&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;3&lt;/str&gt;
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Acura&lt;/str&gt;
      &lt;str&gt;RDX&lt;/str&gt;
      &lt;str&gt;2003&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;4&lt;/str&gt;
  &lt;/doc&gt;
&lt;/result&gt;
&lt;lst name="debug"&gt;
  &lt;str name="rawquerystring"&gt;*dx&lt;/str&gt;
  &lt;str name="querystring"&gt;*dx&lt;/str&gt;
  &lt;str name="parsedquery"&gt;content:#1;xd*&lt;/str&gt;
  &lt;str name="parsedquery_toString"&gt;content:#1;xd*&lt;/str&gt;
  ...
&lt;/lst&gt;</pre>
<p lang="pl-PL">Użyliśmy „dzikiej karty” „*” na początku zapytania (pozycja = 1) i dodatkowo mamy jeszcze dwa znaki po ostatniej „dzikiej karcie”. Filtr użył zatem tokenów odwróconych:</p>
<pre class="brush:xml">&lt;str name="parsedquery"&gt;content:#1;xd*&lt;/str&gt;</pre>
<p lang="pl-PL">Jak widzimy, tokeny odwrócone są w indeksie poprzedzone specjalnym prefixem, aby nie doszło do wyszukania nieprawidłowych dokumentów.</p>
</li>
<li>?q=r?x&amp;fl=id,content&amp;debugQuery=on
<pre class="brush:xml">&lt;result name="response" numFound="2" start="0"&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Acura&lt;/str&gt;
      &lt;str&gt;RDX&lt;/str&gt;
      &lt;str&gt;2003&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;4&lt;/str&gt;
  &lt;/doc&gt;
  &lt;doc&gt;
    &lt;arr name="content"&gt;
      &lt;str&gt;Acura&lt;/str&gt;
      &lt;str&gt;RSX&lt;/str&gt;
      &lt;str&gt;2006&lt;/str&gt;
    &lt;/arr&gt;
    &lt;str name="id"&gt;5&lt;/str&gt;
  &lt;/doc&gt;
&lt;/result&gt;
&lt;lst name="debug"&gt;
  &lt;str name="rawquerystring"&gt;r?x&lt;/str&gt;
  &lt;str name="querystring"&gt;r?x&lt;/str&gt;
  &lt;str name="parsedquery"&gt;content:r?x&lt;/str&gt;
  &lt;str name="parsedquery_toString"&gt;content:r?x&lt;/str&gt;
  ...
&lt;/lst&gt;</pre>
<p lang="pl-PL">Użyliśmy „dzikiej karty” „?” na pozycji 2 oraz dodatkowo mamy tylko jeden znak występujący po ostatniej „dzikiej karcie”. Zatem filtr do wyszukiwania użył tokenów oryginalnych:</p>
<pre class="brush:xml">&lt;str name="parsedquery"&gt;content:r?x&lt;&lt;/str&gt;</pre>
</li>
</ol>
<h3>Podsumowanie</h3>
<p lang="pl-PL">Dzięki filtrowi solr.ReversedWildcardFilter zoptymalizowaliśmy zapytania z „dzikimi kartami”, zatem nasi użytkownicy mogą teraz efektywnie z takich zapytań korzystać <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/10/10/aplikacja-sprzedaz-samochodow-solr-reversedwildcardfilter-czyli-optymalizujemy-zapytania-wildcard-cz-8/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja „sprzedaż samochodów” – Result Grouping, dodanie kolejnych dwóch parametrów (cz. 7)</title>
		<link>https://solr.pl/2011/08/01/aplikacja-sprzedaz-samochodow-result-grouping-dodanie-kolejnych-dwoch-parametrow-cz-7/</link>
					<comments>https://solr.pl/2011/08/01/aplikacja-sprzedaz-samochodow-result-grouping-dodanie-kolejnych-dwoch-parametrow-cz-7/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 01 Aug 2011 19:18:49 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=311</guid>

					<description><![CDATA[W poprzednim poście z tej serii opisaliśmy funkcjonalność grupowania wyników wyszukiwania. Dzisiaj chciałbym pokazać jak łatwo możemy ustalić ilość wygenerowanych grup i jak sortować dokumenty wewnątrz grupy. Specyfikacja wymagań Chciałbym stworzyć zapytanie grupujące, które w odpowiedzi pokaże mi ilość wygenerowanych]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">W poprzednim poście z tej serii opisaliśmy funkcjonalność grupowania wyników wyszukiwania. Dzisiaj chciałbym pokazać jak łatwo możemy ustalić ilość wygenerowanych grup i jak sortować dokumenty wewnątrz grupy.</p>
<p><span id="more-311"></span></p>
<h3>Specyfikacja wymagań</h3>
<p lang="pl-PL">Chciałbym stworzyć zapytanie grupujące, które w odpowiedzi pokaże mi ilość wygenerowanych grup, oraz dostarczy po jednym dokumencie w każdej grupie – dokumencie określającym auto o najniższej cenie w swojej grupie rocznikowej.</p>
<h3>Opis parametrów requestu nowej funkcjonalności</h3>
<p lang="pl-PL">Potrzebujemy następujących parametrów:</p>
<ul>
<li><em>group.ngroups</em> &#8211; parametr typu logicznego, który pozwoli nam na zawarcie w odpowiedzi ilości wygenerowanych grup</li>
<li><em>group.sort</em> &#8211; parametr opisujący sposób sortowania dokumentów wewnątrz grupy</li>
</ul>
<h3>Tworzymy zapytanie</h3>
<p lang="pl-PL">Posługując się zapytaniem z poprzedniego postu, dodajemy dwa nowe parametry:</p>
<pre class="brush:xml">?q=audi+a4&amp;group=true&amp;group.field=year_group&amp;group.limit=1&amp;fl=id,mileage,make,model,year,price&amp;group.ngroups=true&amp;group.sort=price+asc</pre>
<p lang="pl-PL">Zauważmy, że poza dodaniem parametrów group.ngroups oraz group.sort, ustawiliśmy także wartość parametru group.limit na 1 (tak abyśmy otrzymali tylko jeden dokument w każdej grupie) oraz dodaliśmy do parametru fl pole określające cenę auta. W rezultacie otrzymujemy:</p>
<pre class="brush:xml">&lt;lst name="grouped"&gt;
  &lt;lst name="year_group"&gt;
    &lt;int name="matches"&gt;5&lt;/int&gt;
    &lt;int name="ngroups"&gt;3&lt;/int&gt;
    &lt;arr name="groups"&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2002&lt;/str&gt;
        &lt;result name="doclist" numFound="2" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;3&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;125000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;float name="price"&gt;21300.0&lt;/float&gt;
            &lt;int name="year"&gt;2002&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2003&lt;/str&gt;
        &lt;result name="doclist" numFound="2" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;2&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;220000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;float name="price"&gt;27800.0&lt;/float&gt;
            &lt;int name="year"&gt;2003&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2006&lt;/str&gt;
        &lt;result name="doclist" numFound="1" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;5&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;9900&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;float name="price"&gt;32100.0&lt;/float&gt;
            &lt;int name="year"&gt;2006&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
    &lt;/arr&gt;
  &lt;/lst&gt;
&lt;/lst&gt;</pre>
<p lang="pl-PL">W odpowiedz pojawił nam się nowy element, określający ilość wygenerowanych grup:</p>
<pre class="brush:xml">&lt;int name="ngroups"&gt;3&lt;/int&gt;</pre>
<p lang="pl-PL">W każdej grupie rocznikowej mamy po jednym dokumencie, jest to auto o najniższej cenie w swojej grupie. Nie wierzysz ? Przeanalizuj odpowiedzi zawarte w poprzednim poście i porównaj ceny <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3>Podsumowanie</h3>
<p lang="pl-PL">Był to szybki przegląd dwóch nowych parametrów powiązanych z funkcjonalnością grupowania wyników. Duże podziękowania dla Davida Martina za dostarczenie mi tematu poprzez dyskusję na temat poprzedniego postu <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/08/01/aplikacja-sprzedaz-samochodow-result-grouping-dodanie-kolejnych-dwoch-parametrow-cz-7/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja „sprzedaż samochodów” – Result Grouping, czyli grupowanie wyników wyszukiwania (cz. 6)</title>
		<link>https://solr.pl/2011/07/04/aplikacja-sprzedaz-samochodow-result-grouping-czyli-grupowanie-wynikow-wyszukiwania-cz-6/</link>
					<comments>https://solr.pl/2011/07/04/aplikacja-sprzedaz-samochodow-result-grouping-czyli-grupowanie-wynikow-wyszukiwania-cz-6/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 04 Jul 2011 19:12:42 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=304</guid>

					<description><![CDATA[W dzisiejszym poście postaramy się dodać do naszej aplikacji sprzedaży samochodów kolejną funkcjonalność, która będzie polegała na grupowaniu wyników wyszukiwania. Wyobraźmy sobie sytuację, że użytkownik chciałby na zapytanie „Audi A4” otrzymać wyniki pogrupowane np. po roku produkcji, tak aby widział]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">W dzisiejszym poście postaramy się dodać do naszej aplikacji sprzedaży samochodów kolejną funkcjonalność, która będzie polegała na grupowaniu wyników wyszukiwania. Wyobraźmy sobie sytuację, że użytkownik chciałby na zapytanie „Audi A4” otrzymać wyniki pogrupowane np. po roku produkcji, tak aby widział po 2-3 wyniki wyszukiwania dla każdego roku. A może grupowanie po zakresach przebiegu auta ? Zajmijmy się tym tematem.</p>
<p><span id="more-304"></span></p>
<h3>Opis parametrów requestu nowej funkcjonalności</h3>
<p lang="pl-PL">Funkcjonalność grupowania wyników wyszukiwania jest dostępna od wersji solr 3.3. Przyjrzyjmy się podstawowym parametrom requestu, jakich będziemy potrzebowali:</p>
<ul>
<li><em>group</em> &#8211; włącza/wyłącza grupowanie wyników</li>
<li><em>group.field</em> &#8211; nazwa pola, po którym chcemy pogrupować wyniki. Musimy zadbać o to aby pole, po którym chcemy grupować (rok produkcji), było w postaci tekstowej i nie było polem wielokrotnym</li>
<li><em>group.query</em> &#8211; zapytanie, które użyjemy w celu pogrupowania wyników po zakresie przebiegu auta</li>
<li><em>group.limit</em> &#8211; limit wyników wyszukiwania w każdej z grup</li>
</ul>
<p lang="pl-PL">Te cztery podstawowe parametry pozwolą nam na zrealizowanie założeń.</p>
<h3>Zmiany w schema.xml</h3>
<p lang="pl-PL">Ewentualne zmiany w pliku schema.xml będą polegały na zadbaniu, aby pola, po których chcemy grupować wyniki wyszukiwania, było w postaci „string” lub „text”. Nasze wyniki chcielibyśmy grupować po polu „rok produkcji”. Dla przypomnienia, reprezentacja tego pola w tym momencie wygląda tak:</p>
<pre class="brush:xml">&lt;field name="year" type="tint" indexed="true" stored="true" required="true" /&gt;</pre>
<p lang="pl-PL">czyli jest to pole typu całkowitoliczbowego. W celu umożliwienia grupowania po tym polu tworzymy kolejne pole, które będzie odpowiednikiem pola „year”, ale w postaci tekstowej:</p>
<pre class="brush:xml">&lt;field name="year_group" type="string" indexed="true" stored="false" /&gt;</pre>
<p lang="pl-PL">i kopiujemy zawartość pola „year” do pola „year_group”:</p>
<pre class="brush:xml">&lt;copyField source="year" dest="year_group"/&gt;</pre>
<p lang="pl-PL">To praktycznie wszystkie zmiany jakie musimy dokonać w pliku konfiguracyjnym schema.xml.</p>
<h3>Przykładowe dane</h3>
<p lang="pl-PL">Stwórzmy teraz przykładowe dane w celu przetestowania nowej funkcjonalności. Załóżmy że mamy próbkę danych aut o marce Audi i modelu A4. Dwa z nich są z rocznika 2002, kolejne dwa z rocznika 2003 oraz jeden z rocznika 2006. Dodatkowo, jedno z aut ma przebieg poniżej 100 000 km, trzy mają przebieg od 100 000 do 199 999 km i jedno auto które ma przebieg co najmniej 200 000 km:</p>
<pre class="brush:xml">&lt;add&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;1&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A4&lt;/field&gt;
      &lt;field name="year"&gt;2002&lt;/field&gt;
      &lt;field name="price"&gt;22700&lt;/field&gt;
      &lt;field name="engine_size"&gt;1900&lt;/field&gt;
      &lt;field name="mileage"&gt;197000&lt;/field&gt;
      &lt;field name="colour"&gt;green&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Koszalin&lt;/field&gt;
      &lt;field name="loc"&gt;54.12,16.11&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;2&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A4&lt;/field&gt;
      &lt;field name="year"&gt;2003&lt;/field&gt;
      &lt;field name="price"&gt;27800&lt;/field&gt;
      &lt;field name="engine_size"&gt;1900&lt;/field&gt;
      &lt;field name="mileage"&gt;220000&lt;/field&gt;
      &lt;field name="colour"&gt;black&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Bialystok&lt;/field&gt;
      &lt;field name="loc"&gt;53.08,23.09&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;3&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A4&lt;/field&gt;
      &lt;field name="year"&gt;2002&lt;/field&gt;
      &lt;field name="price"&gt;21300&lt;/field&gt;
      &lt;field name="engine_size"&gt;1900&lt;/field&gt;
      &lt;field name="mileage"&gt;125000&lt;/field&gt;
      &lt;field name="colour"&gt;black&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Szczecin&lt;/field&gt;
      &lt;field name="loc"&gt;53.25,14.35&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;4&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A4&lt;/field&gt;
      &lt;field name="year"&gt;2003&lt;/field&gt;
      &lt;field name="price"&gt;30300&lt;/field&gt;
      &lt;field name="engine_size"&gt;1900&lt;/field&gt;
      &lt;field name="mileage"&gt;150000&lt;/field&gt;
      &lt;field name="colour"&gt;red&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Gdansk&lt;/field&gt;
      &lt;field name="loc"&gt;54.21,18.40&lt;/field&gt;
   &lt;/doc&gt;
  &lt;doc&gt;
      &lt;field name="id"&gt;5&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A4&lt;/field&gt;
      &lt;field name="year"&gt;2006&lt;/field&gt;
      &lt;field name="price"&gt;32100&lt;/field&gt;
      &lt;field name="engine_size"&gt;1900&lt;/field&gt;
      &lt;field name="mileage"&gt;9900&lt;/field&gt;
      &lt;field name="colour"&gt;red&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Swidnik&lt;/field&gt;
      &lt;field name="loc"&gt;52.15,21.00&lt;/field&gt;
   &lt;/doc&gt;
&lt;/add&gt;</pre>
<h3>Tworzymy zapytania</h3>
<p lang="pl-PL">Wykorzystując parametry opisane na początku artykułu, tworzymy zapytanie, które zwróci nam wyniki wyszukiwania dla zapytania „Audi A4” pogrupowane po roku produkcji auta:</p>
<pre class="brush:xml">?q=audi+a4&amp;group=true&amp;group.field=year_group&amp;group.limit=2&amp;fl=id,mileage,make,model,year</pre>
<p lang="pl-PL">Jak widać, ograniczyliśmy nasze wyniki wyszukiwania do maksymalnie dwóch w każdej z grup. Wypiszemy sobie w response jedynie te pola, które dadzą nam czytelny obraz identyfikacji dokumentów, czyli identyfikator, przebieg, marka, model oraz rok produkcji. W rezultacie otrzymujemy w response:</p>
<pre class="brush:xml">&lt;lst name="grouped"&gt;
  &lt;lst name="year_group"&gt;
    &lt;int name="matches"&gt;5&lt;/int&gt;
    &lt;arr name="groups"&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2002&lt;/str&gt;
        &lt;result name="doclist" numFound="2" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;1&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;197000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;int name="year"&gt;2002&lt;/int&gt;
          &lt;/doc&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;3&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;125000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;int name="year"&gt;2002&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2003&lt;/str&gt;
        &lt;result name="doclist" numFound="2" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;2&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;220000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;int name="year"&gt;2003&lt;/int&gt;
          &lt;/doc&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;4&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;150000&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;int name="year"&gt;2003&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
      &lt;lst&gt;
        &lt;str name="groupValue"&gt;2006&lt;/str&gt;
        &lt;result name="doclist" numFound="1" start="0"&gt;
          &lt;doc&gt;
            &lt;str name="id"&gt;5&lt;/str&gt;
            &lt;str name="make"&gt;Audi&lt;/str&gt;
            &lt;int name="mileage"&gt;9900&lt;/int&gt;
            &lt;str name="model"&gt;A4&lt;/str&gt;
            &lt;int name="year"&gt;2006&lt;/int&gt;
          &lt;/doc&gt;
        &lt;/result&gt;
      &lt;/lst&gt;
    &lt;/arr&gt;
  &lt;/lst&gt;
&lt;/lst&gt;</pre>
<p lang="pl-PL">Przeanalizujmy sobie odpowiedź. Na dane zapytanie otrzymaliśmy 5 trafień:</p>
<pre class="brush:xml">&lt;int name="matches"&gt;5&lt;/int&gt;</pre>
<p lang="pl-PL">Odpowiedź została podzielona na 3 niezależne grupy:</p>
<ol>
<li>
<pre class="brush:xml">&lt;str name="groupValue"&gt;2002&lt;/str&gt;</pre>
<p>w której znalazły się dwa dokumenty (numFound=&#8221;2&#8243;), czyli auta z rocznika 2002</p>
</li>
<li>
<pre class="brush:xml">&lt;str name="groupValue"&gt;2003&lt;/str&gt;</pre>
<p>w której znalazły się dwa dokumenty (numFound=&#8221;2&#8243;), czyli auta z rocznika 2003</p>
</li>
<li>
<pre class="brush:xml">&lt;str name="groupValue"&gt;2006&lt;/str&gt;</pre>
<p>w której znalazł się jeden dokument (numFound=&#8221;1&#8243;), czyli auto z rocznika 2006</p>
</li>
</ol>
<p lang="pl-PL">Zgadza się!</p>
<p lang="pl-PL">Skonstruujmy teraz zapytanie, które pogrupuje nam wyniki wyszukiwania po zakresie przebiegu samochodu. Zakładamy 3 zakresy:</p>
<ol>
<li>&lt;0km ; 99999km&gt;</li>
<li>&lt;100000km ; 199999km&gt;</li>
<li>&lt;200000km ; * &gt;</li>
</ol>
<p lang="pl-PL">Zapytanie:</p>
<pre class="brush:xml">?q=audi+a4&amp;group=true&amp;group.query=mileage:[0+TO+99999]&amp;group.query=mileage:[100000+TO+199999]&amp;group.query=mileage:[200000+TO+*]&amp;group.limit=3&amp;fl=id,mileage,make,model,year</pre>
<p lang="pl-PL">Dostajemy odpowiedź:</p>
<pre class="brush:xml">&lt;lst name="grouped"&gt;
  &lt;lst name="mileage:[0 TO 99999]"&gt;
    &lt;int name="matches"&gt;5&lt;/int&gt;
    &lt;result name="doclist" numFound="1" start="0"&gt;
      &lt;doc&gt;
        &lt;str name="id"&gt;5&lt;/str&gt;
        &lt;str name="make"&gt;Audi&lt;/str&gt;
        &lt;int name="mileage"&gt;9900&lt;/int&gt;
        &lt;str name="model"&gt;A4&lt;/str&gt;
        &lt;int name="year"&gt;2006&lt;/int&gt;
      &lt;/doc&gt;
    &lt;/result&gt;
  &lt;/lst&gt;
  &lt;lst name="mileage:[100000 TO 199999]"&gt;
    &lt;int name="matches"&gt;5&lt;/int&gt;
    &lt;result name="doclist" numFound="3" start="0"&gt;
      &lt;doc&gt;
        &lt;str name="id"&gt;1&lt;/str&gt;
        &lt;str name="make"&gt;Audi&lt;/str&gt;
        &lt;int name="mileage"&gt;197000&lt;/int&gt;
        &lt;str name="model"&gt;A4&lt;/str&gt;
        &lt;int name="year"&gt;2002&lt;/int&gt;
      &lt;/doc&gt;
      &lt;doc&gt;
        &lt;str name="id"&gt;3&lt;/str&gt;
        &lt;str name="make"&gt;Audi&lt;/str&gt;
        &lt;int name="mileage"&gt;125000&lt;/int&gt;
        &lt;str name="model"&gt;A4&lt;/str&gt;
        &lt;int name="year"&gt;2002&lt;/int&gt;
      &lt;/doc&gt;
      &lt;doc&gt;
        &lt;str name="id"&gt;4&lt;/str&gt;
        &lt;str name="make"&gt;Audi&lt;/str&gt;
        &lt;int name="mileage"&gt;150000&lt;/int&gt;
        &lt;str name="model"&gt;A4&lt;/str&gt;
        &lt;int name="year"&gt;2003&lt;/int&gt;
      &lt;/doc&gt;
    &lt;/result&gt;
  &lt;/lst&gt;
  &lt;lst name="mileage:[200000 TO *]"&gt;
    &lt;int name="matches"&gt;5&lt;/int&gt;
    &lt;result name="doclist" numFound="1" start="0"&gt;
      &lt;doc&gt;
        &lt;str name="id"&gt;2&lt;/str&gt;
        &lt;str name="make"&gt;Audi&lt;/str&gt;
        &lt;int name="mileage"&gt;220000&lt;/int&gt;
        &lt;str name="model"&gt;A4&lt;/str&gt;
        &lt;int name="year"&gt;2003&lt;/int&gt;
      &lt;/doc&gt;
    &lt;/result&gt;
  &lt;/lst&gt;
&lt;/lst&gt;</pre>
<p lang="pl-PL">Ponownie otrzymaliśmy 5 wyników. W pierwszej grupie znalazło się auto o przebiegu 9900 km, w drugiej grupie auta o przebiegach 197000 km, 125000 km oraz 150000 km, a w trzeciej auto o największym przebiegu, czyli 220000 km.<br />
Otrzymaliśmy to co chcieliśmy osiągnąć. Zadanie wykonane.</p>
<h3>Podsumowanie</h3>
<p lang="pl-PL">Kolejna funkcjonalność, tym razem związana z grupowaniem wyników wyszukiwania, została dodana do naszej aplikacji sprzedaży samochodów. Zobaczymy jak zareagują na nią klienci <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/07/04/aplikacja-sprzedaz-samochodow-result-grouping-czyli-grupowanie-wynikow-wyszukiwania-cz-6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja „sprzedaż samochodów” – SpellCheckComponent &#8211; czy naprawdę miałeś to na myśli ? (cz. 5)</title>
		<link>https://solr.pl/2011/05/23/aplikacja-sprzedaz-samochodow-spellcheckcomponent-czy-naprawde-miales-to-na-mysli-cz-5/</link>
					<comments>https://solr.pl/2011/05/23/aplikacja-sprzedaz-samochodow-spellcheckcomponent-czy-naprawde-miales-to-na-mysli-cz-5/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 23 May 2011 17:46:02 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=242</guid>

					<description><![CDATA[Nadszedł czas, abyśmy dodali do naszej aplikacji sprzedaży samochodów kolejną ważną funkcjonalność. Będzie to mechanizm sprawdzania poprawności wpisanej frazy wyszukiwania oraz podpowiadania frazy poprawnej. Funkcjonalność ta stała się już standardem we wszystkich silnikach wyszukiwania, zatem i my zrobimy z niej]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">Nadszedł czas, abyśmy dodali do naszej aplikacji sprzedaży samochodów kolejną ważną funkcjonalność. Będzie to mechanizm sprawdzania poprawności wpisanej frazy wyszukiwania oraz podpowiadania frazy poprawnej. Funkcjonalność ta stała się już standardem we wszystkich silnikach wyszukiwania, zatem i my zrobimy z niej użytek.</p>
<p><span id="more-242"></span></p>
<h2>Analiza wymagań</h2>
<p lang="pl-PL">Nasza baza samochodów jest już tak duża, że zawiera nazwy marek i modeli, których poprawne napisanie może sprawiać naszym klientom problemy, np:</p>
<ol>
<li>
<ul>
<li><em>marka</em>: Bugatti</li>
<li><em>model</em>: Veyron</li>
</ul>
</li>
<li>
<ul>
<li><em>marka</em>: Daewoo</li>
<li><em>model</em>: Lacetti</li>
</ul>
</li>
<li>
<ul>
<li><em>marka</em>: Cadillac</li>
<li><em>model</em>: Brougham</li>
</ul>
</li>
<li>
<ul>
<li><em>marka</em>: Ford</li>
<li><em>model</em>: Capri</li>
</ul>
</li>
<li>
<ul>
<li><em>marka</em>: Maserati</li>
<li><em>model</em>: Coupe</li>
</ul>
</li>
</ol>
<p lang="pl-PL">Przykłady zapytań, które zwróciły 0 wyników, ze względu na niepoprawnie wpisane nazwy (frazy):</p>
<ol>
<li>?q=bugati+weyron</li>
<li>?q=daewo+laceti</li>
<li>?q=cadilac+brogham</li>
<li>?q=ford+kapri</li>
<li>?q=maseratti+coupe</li>
</ol>
<p lang="pl-PL">Chcemy dodać funkcjonalność, która przy niepoprawnie wpisanych nazwach podpowie nam frazę, którą najprawdopodobniej klient miał na myśli, a której zastosowanie pozwoli nam wyszukać dokumenty związane z tą frazą.</p>
<h2>Zmiany w solrconfig.xml</h2>
<p lang="pl-PL">Najważniejszym elementem, który musimy dodać do pliku konfiguracyjnego solrconfig.xml jest komponent wykorzystujący klasę <em>solr.SpellCheckComponent</em>. Spróbujemy wykorzystać najprostszą, standardową konfigurację tego komponentu i przekonamy się, jak jego działanie sprawdzi się w praktyce:</p>
<pre class="brush:xml">&lt;searchComponent name="spellcheck" class="solr.SpellCheckComponent"&gt;
    &lt;lst name="spellchecker"&gt;
      &lt;str name="classname"&gt;solr.IndexBasedSpellChecker&lt;/str&gt;
      &lt;str name="spellcheckIndexDir"&gt;./spellchecker&lt;/str&gt;
      &lt;str name="field"&gt;content&lt;/str&gt;
      &lt;str name="buildOnCommit"&gt;true&lt;/str&gt;
    &lt;/lst&gt;
&lt;/searchComponent&gt;</pre>
<p lang="pl-PL">Wyjaśnijmy sobie co znaczą poszczególne atrybuty:</p>
<ol>
<li>
<ul>
<li>
<p lang="pl-PL"><em>classname</em> – klasa która jest implementacją naszego mechanizmu podpowiadania poprawnej frazy wyszukiwania. Wykorzystujemy klasę solr.IndexBasedSpellChecker, która jako źródło podpowiedzi wykorzystuje indeks solr.</p>
</li>
</ul>
<ul>
<li>
<p lang="pl-PL"><em>spellcheckIndexDir</em> – katalog, w którym przechowywany będzie indeks mechanizmu popowiedzi.</p>
</li>
</ul>
<ul>
<li>
<p lang="pl-PL"><em>field</em> – nazwa pola zdefiniowanego w pliku schema.xml, wykorzystywanego jako pole źródłowe do generowania indeksu dla mechanizmu podpowiedzi. W naszym przypadku będzie to pole o nazwie „content”, co zostanie uzasadnionej później.</p>
</li>
</ul>
<ul>
<li>
<p lang="pl-PL"><em>buildOnCommit</em> – jeżeli atrybut ten będzie ustawiony na wartośc <em>true</em>, to indeks mechanizmu podpowiedzi zostanie automatycznie wygenerowany przy każdym uaktualnieniu (commit) indeksu solr.</p>
</li>
</ul>
</li>
</ol>
<p lang="pl-PL">Mamy już zdefiniowany komponent, zatem teraz należy go wykorzystać w którymś z handler&#8217;ów, aby można było się do niego odwoływać. Najlepiej dodać go do handler&#8217;a, którego domyślnie używamy do wyszukiwania dokumentów. W ten sposób będziemy mogli za pomocą tylko jednego żądania otrzymywać wyniki wyszukiwania wraz z podpowiedzią. Przed uaktualnieniem, nasz domyślny handler wyglądał tak:</p>
<pre class="brush:xml">&lt;requestHandler name="standard" class="solr.SearchHandler" default="true"&gt;
     &lt;lst name="defaults"&gt;
       &lt;str name="echoParams"&gt;explicit&lt;/str&gt;
     &lt;/lst&gt;
&lt;/requestHandler&gt;</pre>
<p lang="pl-PL">Po zmianie, wygląda tak:</p>
<pre class="brush:xml">&lt;requestHandler name="standard" class="solr.SearchHandler" default="true"&gt;
     &lt;lst name="defaults"&gt;
       &lt;str name="echoParams"&gt;explicit&lt;/str&gt;
       &lt;str name="spellcheck"&gt;true&lt;/str&gt;
       &lt;str name="spellcheck.collate"&gt;true&lt;/str&gt;
     &lt;/lst&gt;
     &lt;arr name="last-components"&gt;
       &lt;str&gt;spellcheck&lt;/str&gt;
     &lt;/arr&gt;
&lt;/requestHandler&gt;</pre>
<p lang="pl-PL">Jak widać, oprócz naszego komponentu spellcheck, dodaliśmy również dwie domyślne wartość wykorzystywane w zapytaniach:</p>
<ol>
<li>
<ul>
<li>
<p lang="pl-PL"><em>spellcheck</em> – ustawienie wartości na <em>true</em> powoduje że dla każdego requestu nastąpi próba wygenerowania podpowiedzi.</p>
</li>
</ul>
<ul>
<li>
<p lang="pl-PL"><em>spellcheck.collate</em> &#8211; ustawienie wartości na <em>true</em> powoduje że mechanizm wybiera najlepszą podpowiedź dla każdego wyrazu i konstruuje nowe zapytanie składające się z tych podpowiedzi. Jeżeli mechanizm uzna, że dany wyraz jest poprawny, zostawia go w niezmienionej postaci.</p>
</li>
</ul>
</li>
</ol>
<h2>Zmiany w schema.xml</h2>
<p lang="pl-PL">Ewentualne zmiany w pliku schema.xml będą polegały na dodaniu pola, wykorzystywanego przez komponent <em>solr.SpellCheckComponent</em> jako źródło danych do generowania indeksu dla mechanizmu podpowiedzi. Pole takie powinno zawierać wszystkie informacje, jakie chcielibyśmy aby było użyte przy tworzeniu indeksu dla mechanizmu podpowiedzi. Typ takiego pola powinien zapewniać odpowiednią tokenizację indeksowanych danych, jak i być pozbawionym wszelkich filtrów używających stemmingu czy lametyzacji, co by mogło niekorzystnie wpłynąć na wyniki podpowiedzi.</p>
<p lang="pl-PL">Nasza schema posiada już pole spełniające wszystkie te wymaganie, a nazywa się  „content”. Dla przypomnienia, jest to pole domyślne, po którym realizowane jest wyszukiwanie przez silnik solr. Przypomnijmy sobie aktualną definicję tego pola, jak i jego typu:</p>
<pre class="brush:xml">&lt;field name="content" type="text" indexed="true" stored="false" multiValued="true"/&gt;</pre>
<pre class="brush:xml">&lt;fieldType name="text" positionIncrementGap="100"&gt;
 &lt;analyzer&gt;
  &lt;tokenizer class="solr.WhitespaceTokenizerFactory"/&gt;
   &lt;filter class="solr.PatternReplaceFilterFactory" pattern="'" replacement="" replace="all" /&gt;
   &lt;filter class="solr.WordDelimiterFilterFactory"
    generateWordParts="1"
    generateNumberParts="1"
    catenateWords="1"
    stemEnglishPossessive="0"
  /&gt;
  &lt;filter class="solr.LowerCaseFilterFactory"/&gt;
 &lt;/analyzer&gt;
&lt;/fieldType&gt;</pre>
<p lang="pl-PL">Do pola &#8222;content&#8221; kopiowane są wartości z pól marki, modelu i roku:</p>
<pre class="brush:xml">&lt;copyField source="make" dest="content"/&gt;
&lt;copyField source="model" dest="content"/&gt;
&lt;copyField source="year" dest="content"/&gt;</pre>
<h2>Tworzymy zapytania</h2>
<p lang="pl-PL">Wykorzystamy zapytania z analizy wymagań, które nie zwróciły nam żadnych wyników, dodając parametr <em>spellcheck.q</em>, gdzie wpisujemy tę samą frazę co dla parametru <em>q</em>. W ten sposób, za pomocą jednego zapytania zwrócimy wyniki wyszukiwania wraz z wynikami mechanizmu podpowiedzi:</p>
<ol>
<li>?q=bugati+weyron&amp;spellcheck.q=bugati+weyron
<ul>
<pre class="brush:xml">&lt;result name="response" numFound="0" start="0" /&gt;
&lt;lst name="spellcheck"&gt;
  &lt;lst name="suggestions"&gt;
    &lt;lst name="bugati"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;0&lt;/int&gt;
      &lt;int name="endOffset"&gt;6&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;bugatti&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
    &lt;lst name="weyron"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;7&lt;/int&gt;
      &lt;int name="endOffset"&gt;13&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;veyron&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
      &lt;str name="collation"&gt;bugatti veyron&lt;/str&gt;
    &lt;/lst&gt;
&lt;/lst&gt;</pre>
<p lang="pl-PL">Mechanizm podpowiedzi poprawił nam wyrazy zapytania na poprawne i dodatkowo funkcjonalność „collation” wygenerowała nam gotowe zapytanie, które możemy wykorzystać w celach zwrócenia nam poprawnych wyników wyszukiwania. Sprawdźmy kolejne przykłady:</p>
</ul>
</li>
<li>?q=daewo+laceti&amp;spellcheck.q=?q=daewo+laceti
<ul>
<pre class="brush:xml">&lt;result name="response" numFound="0" start="0" /&gt;
&lt;lst name="spellcheck"&gt;
  &lt;lst name="suggestions"&gt;
    &lt;lst name="daewo"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;0&lt;/int&gt;
      &lt;int name="endOffset"&gt;5&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;daewoo&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
    &lt;lst name="laceti"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;6&lt;/int&gt;
      &lt;int name="endOffset"&gt;12&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;lacetti&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
      &lt;str name="collation"&gt;daewoo lacetti&lt;/str&gt;
    &lt;/lst&gt;
&lt;/lst&gt;</pre>
</ul>
</li>
<li>?q=cadilac+brogham&amp;spellcheck.q=cadilac+brogham
<ul>
<pre class="brush:xml">&lt;result name="response" numFound="0" start="0" /&gt;
&lt;lst name="spellcheck"&gt;
  &lt;lst name="suggestions"&gt;
    &lt;lst name="cadilac"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;0&lt;/int&gt;
      &lt;int name="endOffset"&gt;7&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;cadillac&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
    &lt;lst name="brogham"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;8&lt;/int&gt;
      &lt;int name="endOffset"&gt;15&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;brougham&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
      &lt;str name="collation"&gt;cadillac brougham&lt;/str&gt;
    &lt;/lst&gt;
&lt;/lst&gt;</pre>
</ul>
</li>
<li>?q=ford+kapri&amp; spellcheck.q=?q=ford+kapri
<ul>
<pre class="brush:xml">&lt;result name="response" numFound="0" start="0" /&gt;
&lt;lst name="spellcheck"&gt;
  &lt;lst name="suggestions"&gt;
    &lt;lst name="kapri"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;5&lt;/int&gt;
      &lt;int name="endOffset"&gt;10&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;capri&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
      &lt;str name="collation"&gt;ford capri&lt;/str&gt;
    &lt;/lst&gt;
&lt;/lst&gt;</pre>
</ul>
</li>
<li>?q=maseratti+coupe&amp;spellcheck.q=?q=maseratti+coupe
<ul>
<pre class="brush:xml">&lt;result name="response" numFound="0" start="0" /&gt;
&lt;lst name="spellcheck"&gt;
  &lt;lst name="suggestions"&gt;
    &lt;lst name="maseratti"&gt;
      &lt;int name="numFound"&gt;1&lt;/int&gt;
      &lt;int name="startOffset"&gt;0&lt;/int&gt;
      &lt;int name="endOffset"&gt;9&lt;/int&gt;
      &lt;arr name="suggestion"&gt;
        &lt;str&gt;maserati&lt;/str&gt;
      &lt;/arr&gt;
    &lt;/lst&gt;
      &lt;str name="collation"&gt;maserati coupe&lt;/str&gt;
    &lt;/lst&gt;
&lt;/lst&gt;</pre>
</ul>
</li>
</ol>
<p lang="pl-PL">Mechanizm spellcheck zadziałał dla naszych przypadków perfekcyjnie, poprawiając błędnie wpisane wyrazy i generując poprawne zapytanie. W dwóch ostatnich przypadkach (4,5) możemy zaobserwować że mechanizm nie wygenerował podpowiedzi dla poprawnie wpisanych wyrazów (4 – ford, 5 – coupe) lecz wykorzystał je do złożenia poprawnego zapytania (collation).</p>
<h2>Podsumowanie</h2>
<p lang="pl-PL">Nasz silnik wyszukiwania został wzbogacony o funkcjonalność sprawdzania poprawności wpisanej frazy. Zostało nam czekać na opinie klientów &#8230; i być może jakieś uwagi.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/05/23/aplikacja-sprzedaz-samochodow-spellcheckcomponent-czy-naprawde-miales-to-na-mysli-cz-5/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja &#8222;sprzedaż samochodów&#8221; – Unicode Collation, czyli sortowanie wyników wyszukiwania uwzględniając język danych (cz. 4)</title>
		<link>https://solr.pl/2011/04/11/aplikacja-sprzedaz-samochodow-unicode-collation-czyli-sortowanie-wynikow-wyszukiwania-uwzgledniajac-jezyk-danych-cz-4/</link>
					<comments>https://solr.pl/2011/04/11/aplikacja-sprzedaz-samochodow-unicode-collation-czyli-sortowanie-wynikow-wyszukiwania-uwzgledniajac-jezyk-danych-cz-4/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 11 Apr 2011 17:40:09 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=232</guid>

					<description><![CDATA[W trzeciej części cyklu dodaliśmy dane lokalizacyjne oraz informacje o miejscowości, z którego pochodzi auto. Wkrótce potem dodaliśmy również możliwość sortowania po miejscowości, w prosty sposób modyfikując schemę: &#60;field name="city_sort" type="lowercase" indexed="true" stored="false" /&#62; ... &#60;copyField source="city" dest="city_sort"/&#62; Okazało się]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">W <a href="http://solr.pl/2011/03/14/aplikacja-sprzedaz-samochodow-–-spatial-search-czyli-wprowadzenie-danych-lokalizacyjnych-cz-3/" target="_blank" rel="noopener noreferrer">trzeciej części</a> cyklu dodaliśmy dane lokalizacyjne oraz informacje o miejscowości, z którego pochodzi auto. Wkrótce potem dodaliśmy również możliwość sortowania po miejscowości, w prosty sposób modyfikując schemę:</p>
<pre class="brush:xml">&lt;field name="city_sort" type="lowercase" indexed="true" stored="false" /&gt;
...
&lt;copyField source="city" dest="city_sort"/&gt;</pre>
<p lang="pl-PL">Okazało się jednak, że sortowanie po miejscowości (pole city_sort) nie funkcjonuje tak jak powinno, a wszystko ze względu na występowanie w nazwach miast polskich znaków. Co z tym zrobić ?</p>
<h2 lang="pl-PL"><span id="more-232"></span>Analiza wymagań</h2>
<p lang="pl-PL">Sprawdźmy, czy faktycznie sortowanie po polu „city_sort” nie uwzględnia polskich znaków. Zadajmy zapytanie:</p>
<pre class="brush:xml">q=*:*&amp;fl=city&amp;sort=city_sort+asc</pre>
<p lang="pl-PL">Otrzymujemy rezultat:</p>
<pre class="brush:xml">&lt;result name="response" numFound="6" start="0"&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Białystok&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Koszalin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Szczecin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Warszawa&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Świdnik&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Łowicz&lt;/str&gt;
   &lt;/doc&gt;
&lt;/result&gt;</pre>
<p lang="pl-PL">Rzeczywiście, wyniki nie są posortowane poprawnie. Oczekiwaliśmy takiego rezultatu:</p>
<pre class="brush:xml">&lt;result name="response" numFound="6" start="0"&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Białystok&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Koszalin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Łowicz&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Szczecin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Świdnik&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Warszawa&lt;/str&gt;
   &lt;/doc&gt;
&lt;/result&gt;</pre>
<p lang="pl-PL">W celu usprawnienia sortowania danych zawierających polskie znaki wykorzystamy filtr „solr.CollationKeyFilter”.</p>
<h2>solr.CollationKeyFilter</h2>
<p lang="pl-PL">Filtr solr.CollationKeyFilter jest wykorzystywany podczas indeksowania, dodając do indeksu specjalne „klucze sortujące”. Pozwala nam na wskazanie kolatora powiązanego z konkretnym krajem i językiem. Możemy również określić „siłę” kolatora, która definiuje pewien minimalny poziom różnic jaki jest brany pod uwagę przy porównywaniu. Przykład:</p>
<pre class="brush:xml">&lt;filter class="solr.CollationKeyFilterFactory" language="es" country=”ES” strength="primary" /&gt;</pre>
<p lang="pl-PL">Powyższy przykład jest definicją fabryki dla filtra solr.CollationKeyFilter, określającego kolator dla języka hiszpańskiego z siłą <a href="http://download.oracle.com/javase/1.5.0/docs/api/java/text/Collator.html#PRIMARY" target="_blank" rel="noopener noreferrer">primary</a>.</p>
<h2>Zmiany w schema.xml</h2>
<ol>
<li>Definicja nowych typów pól:
<ul>
<pre class="brush:xml">&lt;fieldType name="polishLowercase" positionIncrementGap="100"&gt;
  &lt;analyzer&gt;
    &lt;tokenizer class="solr.KeywordTokenizerFactory"/&gt;
    &lt;filter class="solr.LowerCaseFilterFactory" /&gt;
    &lt;filter class="solr.TrimFilterFactory" /&gt;
    &lt;filter class="solr.CollationKeyFilterFactory"  language="pl" country=”PL” strength="primary" /&gt;
  &lt;/analyzer&gt;
&lt;/fieldType&gt;</pre>
<p>Jak widać jest to tak naprawdę definicja już istniejącego typu „lowercase” uzupełnionego o filtr solr.CollationKeyFilter, określającego kolator dla języka polskiego. Będziemy używać tego typu pola do definiowania pól, które zawierają dane z polskimi znakami.</ul>
</li>
<li>Zmiana definicji pola „city_sort”:
<ul>
<li>zmieniamy typ dla pola „city_sort” na nowo zdefiniowany typ „polishLowercase”:</li>
<pre class="brush:xml">&lt;field name="city_sort" type="polishLowercase" indexed="true" stored="false" /&gt;</pre>
</ul>
</li>
</ol>
<h2>Testy funkcjonalności</h2>
<p lang="pl-PL">Zanim przetestujemy, czy zmiana typu pola przyniosła oczekiwany rezultat, pamiętajmy, że filtr solr.CollationKeyFilter  wykorzystywany w tym typie działa na etapie indeksacji. Należy zatem wykonać pełną reindeksację danych.</p>
<p lang="pl-PL">Sprawdźmy teraz wynik zapytania, które wykorzystaliśmy wcześniej do przetestowania jakości sortowania:</p>
<pre class="brush:xml">q=*:*&amp;fl=city&amp;sort=city_sort+asc</pre>
<p lang="pl-PL">Jak się okazuje, otrzymujemy oczekiwany wcześniej, poprawny rezultat:</p>
<pre class="brush:xml">&lt;result name="response" numFound="6" start="0"&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Białystok&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Koszalin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Łowicz&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Szczecin&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Świdnik&lt;/str&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Warszawa&lt;/str&gt;
   &lt;/doc&gt;
&lt;/result&gt;</pre>
<h2>Podsumowanie</h2>
<p lang="pl-PL">Kolejny zgłoszony problem został pomyślnie rozwiązany. Usprawniliśmy jakość wyników sortowania po polskich znakach, wykorzystując nieskomplikowany, ale jakże pomocny filtr solr.CollationKeyFilter. Czekamy na dalsze zgłoszenia <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/04/11/aplikacja-sprzedaz-samochodow-unicode-collation-czyli-sortowanie-wynikow-wyszukiwania-uwzgledniajac-jezyk-danych-cz-4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja &#8222;sprzedaż samochodów&#8221; – Spatial Search, czyli wprowadzenie danych lokalizacyjnych (cz. 3)</title>
		<link>https://solr.pl/2011/03/14/aplikacja-sprzedaz-samochodow-spatial-search-czyli-wprowadzenie-danych-lokalizacyjnych-cz-3/</link>
					<comments>https://solr.pl/2011/03/14/aplikacja-sprzedaz-samochodow-spatial-search-czyli-wprowadzenie-danych-lokalizacyjnych-cz-3/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 14 Mar 2011 08:13:38 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[schema]]></category>
		<category><![CDATA[solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=207</guid>

					<description><![CDATA[Ilość ogłoszeń w naszej bazie rozrosła się do tego stopnia, że klienci zaproponowali dodanie nowej opcji przy filtrowaniu wyników wyszukiwania oraz nowej opcji sortowania. Mianowicie musimy dodać funkcjonalność, która pozwoli nam operować na danych związanych z lokalizacją auta w danym]]></description>
										<content:encoded><![CDATA[<p lang="pl-PL">Ilość ogłoszeń w naszej bazie rozrosła się do tego stopnia, że klienci zaproponowali dodanie nowej opcji przy filtrowaniu wyników wyszukiwania oraz nowej opcji sortowania. Mianowicie musimy dodać funkcjonalność, która pozwoli nam operować na danych związanych z lokalizacją auta w danym ogłoszeniu.</p>
<p><span id="more-207"></span></p>
<h2>Analiza wymagań</h2>
<p lang="pl-PL">Chcemy dodać dwie nowe funkcjonalności:</p>
<ol>
<li>Zawężanie wyników wyszukiwania w taki sposób, aby możliwe było wyświetlenie tylko tych aut, które są położone nie dalej niż x kilometrów od określonego miejsca, gdzie x = 50,100,200,500,1000 km.</li>
<li>Sortowanie wyników wyszukiwania po odległości pomiędzy danym punktem, a lokalizacją auta z danego ogłoszenia.</li>
</ol>
<p lang="pl-PL">W celu realizacji powyższych zadań, skorzystamy z funkcjonalności solr zwanej &#8222;Spatial Search&#8221;, która dostępna jest od wersji 3.1. Zmiany, które będziemy musieli wprowadzić, dotyczyć będą modyfikacji pliku schema.xml oraz danych wejściowych, do których dodamy informację o położeniu geograficznym każdego z aut. Na końcu zostanie nam już tylko odpowiednie złożenie zapytań.</p>
<p lang="pl-PL">
<h2>Zmiany w schema.xml</h2>
<ol>
<li>Definicja nowych typów pól:
<ul>
<li>pierwsza definicja to nic innego jak kolejny typ liczbowy &#8211; double:</li>
<pre class="brush:xml">&lt;fieldType name="tdouble" precisionStep="8" omitNorms="true" positionIncrementGap="0"/&gt;</pre>
<li> druga definicja zaś wykorzystuje specjalną klasę &#8222;solr.LatLonType&#8221;, która pozwoli nam na zaindeksowanie danych geograficznych wykorzystując pole dynamiczne o suffixie &#8222;_coordinate&#8221;:</li>
<pre class="brush:xml">&lt;fieldType name="location" subFieldSuffix="_coordinate"/&gt;</pre>
</ul>
</li>
<li>Definicja &nbsp;nowych pól:
<ul>
<li>pole, które będzie wykorzystywane do gromadzenia informacji o nazwie miejscowości, z którego pochodzi auto:</li>
<pre class="brush:xml">&lt;field name="city" type="string" indexed="true" stored="true" /&gt;</pre>
<li>pole &#8222;loc&#8221; posłuży nam do zaindeksowania danych lokalizacyjnych:</li>
<pre class="brush:xml">&lt;field name="loc" type="location" indexed="true" stored="false"/&gt;</pre>
<li>pole dynamiczne będzie wykorzystywane wewnętrznie do gromadzenia informacji, które wprowadzimy do pola &#8222;loc&#8221;:</li>
<pre class="brush:xml">&lt;dynamicField name="*_coordinate"  type="tdouble" indexed="true" stored="false"/&gt;</pre>
</ul>
</li>
</ol>
<h2>Analiza danych wejściowych</h2>
<p lang="pl-PL">W celu prezentacji sposobu modyfikacji nowych danych, weźmy próbkę 5-ciu ogłoszeń z miast:</p>
<ol>
<li>Koszalin
<ul>
<li><em>szerokość geograficzna</em>: 54.12</li>
<li><em>długość geograficzna</em>: 16.11</li>
</ul>
</li>
<li>Białystok
<ul>
<li><em>szerokość geograficzna</em>: 53.08</li>
<li><em>długość geograficzna</em>: 23.09</li>
</ul>
</li>
<li>Szczecin
<ul>
<li><em>szerokość geograficzna</em>: 53.25</li>
<li><em>długość geograficzna</em>: 14.35</li>
</ul>
</li>
<li>Gdańsk
<ul>
<li><em>szerokość geograficzna</em>: 54.21</li>
<li><em>długość geograficzna</em>: 18.40</li>
</ul>
</li>
<li>Warszawa
<ul>
<li><em>szerokość geograficzna</em>: 52.15</li>
<li><em>długość geograficzna</em>: 21.00</li>
</ul>
</li>
</ol>
<p lang="pl-PL">Dane lokalizacyjne wprowadzamy do pola &#8222;loc&#8221; wpisując szerokość geograficzną danego miasta oraz po przecinku jego długość. Nasze dane mogą wyglądać zatem tak:</p>
<pre class="brush:xml">&lt;add&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;1&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;80&lt;/field&gt;
      &lt;field name="year"&gt;2008&lt;/field&gt;
      &lt;field name="price"&gt;9774&lt;/field&gt;
      &lt;field name="engine_size"&gt;2000&lt;/field&gt;
      &lt;field name="mileage"&gt;92467&lt;/field&gt;
      &lt;field name="colour"&gt;green&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Koszalin&lt;/field&gt;
      &lt;field name="loc"&gt;54.12,16.11&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;2&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;A8&lt;/field&gt;
      &lt;field name="year"&gt;2009&lt;/field&gt;
      &lt;field name="price"&gt;9078&lt;/field&gt;
      &lt;field name="engine_size"&gt;1000&lt;/field&gt;
      &lt;field name="mileage"&gt;31369&lt;/field&gt;
      &lt;field name="colour"&gt;black&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Białystok&lt;/field&gt;
      &lt;field name="loc"&gt;53.08,23.09&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;3&lt;/field&gt;
      &lt;field name="make"&gt;Audi&lt;/field&gt;
      &lt;field name="model"&gt;TT&lt;/field&gt;
      &lt;field name="year"&gt;1997&lt;/field&gt;
      &lt;field name="price"&gt;1109&lt;/field&gt;
      &lt;field name="engine_size"&gt;1299&lt;/field&gt;
      &lt;field name="mileage"&gt;116987&lt;/field&gt;
      &lt;field name="colour"&gt;silver&lt;/field&gt;
      &lt;field name="damaged"&gt;true&lt;/field&gt;
      &lt;field name="city"&gt;Szczecin&lt;/field&gt;
      &lt;field name="loc"&gt;53.25,14.35&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;4&lt;/field&gt;
      &lt;field name="make"&gt;BMW&lt;/field&gt;
      &lt;field name="model"&gt;Seria 7&lt;/field&gt;
      &lt;field name="year"&gt;2007&lt;/field&gt;
      &lt;field name="price"&gt;140000&lt;/field&gt;
      &lt;field name="engine_size"&gt;3000&lt;/field&gt;
      &lt;field name="mileage"&gt;418000&lt;/field&gt;
      &lt;field name="colour"&gt;green&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Gdańsk&lt;/field&gt;
      &lt;field name="loc"&gt;54.21,18.40&lt;/field&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;field name="id"&gt;5&lt;/field&gt;
      &lt;field name="make"&gt;Chevrolet&lt;/field&gt;
      &lt;field name="model"&gt;TrailBlazer&lt;/field&gt;
      &lt;field name="year"&gt;2007&lt;/field&gt;
      &lt;field name="price"&gt;140000&lt;/field&gt;
      &lt;field name="engine_size"&gt;3000&lt;/field&gt;
      &lt;field name="mileage"&gt;418000&lt;/field&gt;
      &lt;field name="colour"&gt;green&lt;/field&gt;
      &lt;field name="damaged"&gt;false&lt;/field&gt;
      &lt;field name="city"&gt;Warszawa&lt;/field&gt;
      &lt;field name="loc"&gt;52.15,21.00&lt;/field&gt;
   &lt;/doc&gt;
&lt;/add&gt;</pre>
<h2>Tworzymy zapytania</h2>
<p lang="pl-PL">Dane lokalizacyjne mamy w indeksie, zatem zostało nam już tylko złożyć odpowiednie zapytania, które zrealizują nasze nowe funkcjonalności. Załóżmy, że będziemy wyszukiwać ogłoszenia znajdując się w mieście Białystok, które jest położone w odległości ok. 200 km od miasta Warszawa, ok. 400 km od miasta Gdańsk, ok. 550 km od miasta Koszalin oraz ok. 650 km od miasta Szczecin.</p>
<p lang="pl-PL">W celu realizacji punktu 1 z analizy wymagań, dodajemy do żądania nowe zapytanie filtrujące:</p>
<p lang="pl-PL">
<pre class="brush:xml">...&amp;fq={!geofilt sfield=loc}&amp;pt=53.08,23.09&amp;d=50</pre>
<p lang="pl-PL">gdzie:</p>
<ul>
<li><em>sfield</em> &#8211; nazwa pola, do którego wprowadzaliśmy nasze dane lokalizacyjne.</li>
<li><em>pt</em> &#8211; współrzędne punktu startowego, w naszym wypadku są to współrzędne miasta Białystok.</li>
<li><em>d</em> &#8211; dystans o jaki chcemy zawęzić wyniki wyszukiwania. Podstawiając kolejno wartości 50,100,200,500,1000 możemy zrealizować nasze wymagania.</li>
</ul>
<p lang="pl-PL">Przykład:</p>
<ol>
<li>Zapytanie:
<pre class="brush:xml">q=*:*&amp;fq={!geofilt sfield=loc}&amp;pt=53.08,23.09&amp;d=200</pre>
</li>
<li>Wyniki wyszukiwania:</li>
<pre class="brush:xml">&lt;result name="response" numFound="2" start="0"&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Białystok&lt;/str&gt;
      &lt;str name="colour"&gt;black&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;1000&lt;/int&gt;
      &lt;str name="id"&gt;2&lt;/str&gt;
      &lt;str name="make"&gt;Audi&lt;/str&gt;
      &lt;int name="mileage"&gt;31369&lt;/int&gt;
      &lt;str name="model"&gt;A8&lt;/str&gt;
      &lt;float name="price"&gt;9078.0&lt;/float&gt;
      &lt;int name="year"&gt;2009&lt;/int&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Warszawa&lt;/str&gt;
      &lt;str name="colour"&gt;green&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;3000&lt;/int&gt;
      &lt;str name="id"&gt;5&lt;/str&gt;
      &lt;str name="make"&gt;Chevrolet &lt;/str&gt;
      &lt;int name="mileage"&gt;418000&lt;/int&gt;
      &lt;str name="model"&gt;TrailBlazer&lt;/str&gt;
      &lt;float name="price"&gt;140000.0&lt;/float&gt;
      &lt;int name="year"&gt;2007&lt;/int&gt;
   &lt;/doc&gt;
&lt;/result&gt;</pre>
</ol>
<p lang="pl-PL">Świetnie, w wynikach nie mamy ogłoszeń z miast Koszalin, Gdańsk oraz Szczecin, gdyż te miasta leżą w odległości ponad 200 km od miasta Białystok.</p>
<p lang="pl-PL">W celu realizacji punktu 2 z analizy wymagań, wykorzystamy możliwość sortowania wyników wyszukiwania z użyciem funkcji geodist. Tworzymy następujące zapytanie:</p>
<p lang="pl-PL">
<pre class="brush:xml">...&amp;sfield=loc&amp;pt=53.08,23.09&amp;sort=geodist()+desc</pre>
<p lang="pl-PL">Przykład sortowania wyników wyszukiwania po odległości, rozpoczynając od miasta Białystok:</p>
<ol>
<li>Zapytanie:
<pre class="brush:xml">q=*:*&amp;sfield=loc&amp;pt=53.08,23.09&amp;sort=geodist()+asc</pre>
</li>
<li>Wyniki wyszukiwania:</li>
<pre class="brush:xml">&lt;result name="response" numFound="5" start="0"&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Białystok&lt;/str&gt;
      &lt;str name="colour"&gt;black&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;1000&lt;/int&gt;
      &lt;str name="id"&gt;2&lt;/str&gt;
      &lt;str name="make"&gt;Audi&lt;/str&gt;
      &lt;int name="mileage"&gt;31369&lt;/int&gt;
      &lt;str name="model"&gt;A8&lt;/str&gt;
      &lt;float name="price"&gt;9078.0&lt;/float&gt;
      &lt;int name="year"&gt;2009&lt;/int&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Warszawa&lt;/str&gt;
      &lt;str name="colour"&gt;green&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;3000&lt;/int&gt;
      &lt;str name="id"&gt;5&lt;/str&gt;
      &lt;str name="make"&gt;Chevrolet &lt;/str&gt;
      &lt;int name="mileage"&gt;418000&lt;/int&gt;
      &lt;str name="model"&gt;TrailBlazer&lt;/str&gt;
      &lt;float name="price"&gt;140000.0&lt;/float&gt;
      &lt;int name="year"&gt;2007&lt;/int&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Gdańsk&lt;/str&gt;
      &lt;str name="colour"&gt;green&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;3000&lt;/int&gt;
      &lt;str name="id"&gt;4&lt;/str&gt;
      &lt;str name="make"&gt;BMW&lt;/str&gt;
      &lt;int name="mileage"&gt;418000&lt;/int&gt;
      &lt;str name="model"&gt;Seria 7&lt;/str&gt;
      &lt;float name="price"&gt;140000.0&lt;/float&gt;
      &lt;int name="year"&gt;2007&lt;/int&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Koszalin&lt;/str&gt;
      &lt;str name="colour"&gt;green&lt;/str&gt;
      &lt;bool name="damaged"&gt;false&lt;/bool&gt;
      &lt;int name="engine_size"&gt;2000&lt;/int&gt;
      &lt;str name="id"&gt;1&lt;/str&gt;
      &lt;str name="make"&gt;Audi&lt;/str&gt;
      &lt;int name="mileage"&gt;92467&lt;/int&gt;
      &lt;str name="model"&gt;80&lt;/str&gt;
      &lt;float name="price"&gt;9774.0&lt;/float&gt;
      &lt;int name="year"&gt;2008&lt;/int&gt;
   &lt;/doc&gt;
   &lt;doc&gt;
      &lt;str name="city"&gt;Szczecin&lt;/str&gt;
      &lt;str name="colour"&gt;silver&lt;/str&gt;
      &lt;bool name="damaged"&gt;true&lt;/bool&gt;
      &lt;int name="engine_size"&gt;1299&lt;/int&gt;
      &lt;str name="id"&gt;3&lt;/str&gt;
      &lt;str name="make"&gt;Audi&lt;/str&gt;
      &lt;int name="mileage"&gt;116987&lt;/int&gt;
      &lt;str name="model"&gt;TT&lt;/str&gt;
      &lt;float name="price"&gt;1109.0&lt;/float&gt;
      &lt;int name="year"&gt;1997&lt;/int&gt;
   &lt;/doc&gt;
&lt;/result&gt;</pre>
</ol>
<p lang="pl-PL">Zgadza się! Wymagania zostały zrealizowane.</p>
<h2>Podsumowanie</h2>
<p lang="pl-PL">Po raz kolejny udało nam się sprostać oczekiwaniom naszych klientów. Tym razem dodaliśmy funkcjonalności związane z lokalizacją geograficzną aut, które pozwolą użytkownikom na zawężanie oraz sortowanie wyników wyszukiwania wykorzystując odległości geograficzne. Pełen sukces.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/03/14/aplikacja-sprzedaz-samochodow-spatial-search-czyli-wprowadzenie-danych-lokalizacyjnych-cz-3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Aplikacja &#8222;sprzedaż samochodów&#8221; &#8211; projektowanie schema.xml dla naszych potrzeb (cz. 1)</title>
		<link>https://solr.pl/2011/01/31/aplikacja-sprzedaz-samochodow-projektowanie-schema-xml-dla-naszych-potrzeb-cz-1/</link>
					<comments>https://solr.pl/2011/01/31/aplikacja-sprzedaz-samochodow-projektowanie-schema-xml-dla-naszych-potrzeb-cz-1/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Andrzejewski]]></dc:creator>
		<pubDate>Mon, 31 Jan 2011 08:01:46 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[schema]]></category>
		<category><![CDATA[solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=178</guid>

					<description><![CDATA[Podstawowym plikiem konfiguracyjnym solr, który jest niejako łącznikiem pomiędzy tym czego potrzebujemy, a tym co rozumie solr, jest plik schema.xml. Dobre zaprojektowanie schema.xml jest głównym czynnikiem warunkującym poprawne funkcjonowanie wyszukiwarki, która będzie w stanie zrealizować wszystkie wymagania, jakie przed nią]]></description>
										<content:encoded><![CDATA[<p>Podstawowym plikiem konfiguracyjnym solr, który jest niejako łącznikiem pomiędzy tym czego potrzebujemy, a tym co rozumie solr, jest plik schema.xml. Dobre zaprojektowanie schema.xml jest głównym czynnikiem warunkującym poprawne funkcjonowanie wyszukiwarki, która będzie w stanie zrealizować wszystkie wymagania, jakie przed nią stawiamy. Zacznijmy zatem kolejny cykl artykułów, poświęconych projektowaniu pliku schema.xml jak i również wszystkich składników wchodzących w skład zdefiniowanych przez nas typów pól.</p>
<p><span id="more-178"></span></p>
<h2>Analiza wymagań</h2>
<p lang="pl-PL">Wyobraźmy sobie, że chcemy wykorzystać silnik solr w celu dodania wyszukiwarki ogłoszeń samochodowych do naszego serwisu. Serwis nasz jest w tym momencie dosyć prymitywny i przetrzymuje podstawowe informacje opisujące właściwości każdego wystawionego na sprzedaż samochodu:</p>
<ul>
<li>marka</li>
<li>model</li>
<li>rok produkcji</li>
<li>cena</li>
<li>pojemność</li>
<li>przebieg</li>
<li>kolor</li>
<li>czy uszkodzony</li>
</ul>
<p lang="pl-PL">Chcielibyśmy w tym momencie zaprojektować najprostszy plik konfiguracyjny, który pomoże odpowiednio zaindeksować dane z powyższych pól. Zanim jednak przystąpimy do rzeźbienia pliku schema.xml, odpowiedzmy sobie na siedem podstawowych pytań, dotyczących każdego z tych pól:</p>
<h3>1. Jaki typ ?</h3>
<p>Ustalamy typ każdego pola:</p>
<ul>
<li>marka &#8211; pole tekstowe</li>
<li>model &#8211; pole tekstowe</li>
<li>rok produkcji &#8211; pole liczbowe</li>
<li>cena &#8211; pole liczbowe, zmiennoprzecinkowe</li>
<li>pojemność &#8211; pole liczbowe</li>
<li>przebieg &#8211; pole liczbowe</li>
<li>kolor &#8211; pole tekstowe</li>
<li>czy uszkodzony &#8211; pole logiczne</li>
</ul>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Będziemy potrzebowali definicji typów: string, boolean, int, float.</p>
<h3>2. Czy po polu ma się odbywać wyszukiwanie ?</h3>
<p lang="pl-PL">Ustalamy, z których pól będą wykorzystywane informacje, w celu znalezienia odpowiednich ogłoszeń samochodowych. W naszym serwisie będą to 3 pola: marka, model oraz rok produkcji.</p>
<h4>Co nam to mówi ?</h4>
<p>Pewnie będziemy potrzebowali kolejnego typu pola, który będzie poddawany działaniu różnych filtrów, zwiększających nasze szanse znalezienia interesującego nas dokumentu. Stworzymy sobie dodatkowe pole tego typu, po którym będziemy wyszukiwać i wrzucimy tam dane z wszystkich 3 powyższych pól.</p>
<h3>3. Czy sortowalne lub grupowalne ?</h3>
<p lang="pl-PL">Serwis nasz przewiduje sortowanie wyników wyszukiwania po polach: model, rok produkcji, cena oraz przebieg. Chcielibyśmy również móc grupować wyniki wyszukiwania (faceting) po polach marka, model, rok produkcji oraz kolor.</p>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Pola tekstowe, po których sortujemy lub grupujemy nie powinny być poddawane działaniu filtrów, które mogą nam rozdzielić na tokeny wartości w tych polach. Zależy nam jednak na tym, aby wszystkie wartości były zapisane małymi literami, tak aby wielkość liter nie wpływała na sortowanie czy też grupowanie. Będzie trzeba stworzyć nowy typ pola, który nam to umożliwi.</p>
<h3>4. Czy wykorzystywane przy filtrowaniu ?</h3>
<p lang="pl-PL">Na stronie wyszukiwania ogłoszeń chcemy mieć możliwość zawężenia wyników wyszukiwania, poprzez ustawianie zakresów na polach: rok produkcji, cena, pojemność oraz przebieg.</p>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Dobierzmy zatem takie typy dla tych pól, aby filtrowanie po zakresach było jak najbardziej wydajne.</p>
<h3>5. Czy zostały mi pola, które nie zostały wymienione w punktach 2, 3 lub 4 ?</h3>
<p lang="pl-PL">Okazuje się, że na polu &#8222;czy wymagane&#8221; nie będziemy przeprowadzać żadnych &#8222;operacji&#8221;.</p>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Ustawiamy atrybut &#8222;indexed&#8221; dla takiego pola na wartość false.</p>
<h3>6.Czy wymagane ?</h3>
<p lang="pl-PL">W naszym serwisie zakładamy, że polami wymaganymi dla każdego ogłoszenia będą pola marka, model oraz rok produkcji. Nie chcemy mieć dokumentów, które nie mają zdefiniowanych co najmniej tych trzech pól.</p>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Przy definiowaniu tych pól musimy pamiętać o ustawieniu wartości atrybutu &#8222;required&#8221; na true.</p>
<h3>7. Czy wartości pól mają być pobierane z indeksu w oryginalnym stanie?</h3>
<p lang="pl-PL">Informację ze wszystkich pól chcielibyśmy wyciągnąć bezpośrednio z wyników wyszukiwania i zaprezentować klientowi serwisu.</p>
<h4>Co nam to mówi ?</h4>
<p lang="pl-PL">Przy definiowaniu tych pól musimy pamiętać o ustawieniu wartości atrybutu &#8222;stored&#8221; na true.</p>
<h2>Dodajmy definicje typów pól</h2>
<p lang="pl-PL">Odpowiedzieliśmy już sobie na nasze niezbędne pytanie, wyciągnęliśmy wnioski, więc czas wprowadzić je w życie. Dodajmy do schemy typy pól:</p>
<p lang="pl-PL">Dodajemy zwykły typ string, który nie jest poddawany żadnej analizie, przyda się np. jako typ dla pola reprezentującego unikalny identyfikator dokumentu.</p>
<pre class="brush:xml">&lt;fieldType name="string" sortMissingLast="true" omitNorms="true"/&gt;</pre>
<p lang="pl-PL">Dodajemy typ boolean.</p>
<pre class="brush:xml">&lt;fieldType name="boolean" sortMissingLast="true" omitNorms="true"/&gt;</pre>
<p lang="pl-PL">Dodajemy typy dla pól liczbowych. Pamiętamy, że zależy nam na typach, które zagwarantują nam szybsze wykonywanie zapytań po zakresach. Skorzystajmy zatem z typów tint oraz tfloat:</p>
<pre class="brush:xml">&lt;fieldType name="tint" precisionStep="8" omitNorms="true" positionIncrementGap="0"/&gt;
&lt;fieldType name="tfloat" precisionStep="8" omitNorms="true" positionIncrementGap="0"/&gt;</pre>
<p lang="pl-PL">Stwórzmy teraz typ tekstowy, który będzie wykorzystywany przez pole zbiorcze po którym będziemy wyszukiwać. Załóżmy prosty typ, który rozdzieli nam wszystkie tokeny po białych znakach, po czym zamieni wszystkie litery na małe.</p>
<pre class="brush:xml">&lt;fieldType name="text" positionIncrementGap="100"&gt;
  &lt;analyzer&gt;
    &lt;tokenizer class="solr.WhitespaceTokenizerFactory"/&gt;
    &lt;filter class="solr.LowerCaseFilterFactory"/&gt;
  &lt;/analyzer&gt;
&lt;/fieldType&gt;</pre>
<p lang="pl-PL">Potrzebujemy jeszcze typu dla pól, które będą sortowalne/grupowalne:</p>
<pre class="brush:xml">&lt;fieldType name="lowercase" positionIncrementGap="100"&gt;
  &lt;analyzer&gt;
    &lt;tokenizer class="solr.KeywordTokenizerFactory"/&gt;
    &lt;filter class="solr.LowerCaseFilterFactory" /&gt;
    &lt;filter class="solr.TrimFilterFactory" /&gt;
  &lt;/analyzer&gt;
&lt;/fieldType&gt;</pre>
<p lang="pl-PL">KeywordTokenizer tak naprawdę nie tokenizuje wartości którą dostaje na wejściu, czyli niezmieniona wartość zostanie poddana działaniu filtra LowerCaseFilterFactory po czym filtr TrimFilterFactory zadba o to, aby zostały usunięte wszelkie białe znaki, znajdujące się na początku lub na końcu wartości.</p>
<h2>Dodajmy definicje pól</h2>
<p>Identyfikator dokumentu:
</p>
<pre class="brush:xml">&lt;field name="id" type="string" indexed="true" stored="true" required="true" /&gt;</pre>
<p>Marka oraz model:
</p>
<pre class="brush:xml">&lt;field name="make" type="text" indexed="false" stored="true" required="true" /&gt;
&lt;field name="model" type="text" indexed="false" stored="true" required="true" /&gt;</pre>
<p>Nasuwa się pytanie, dlaczego atrybuty indexed dla pól marka oraz model są ustawione na false? Przecież są to pola, które wykorzystywane są przy wyszukiwaniu, sortowaniu i grupowaniu. Zgadza się. Jednakże w celach wyszukiwania przekopiujemy wartości z tych pól do pola zbiorczego, a w celach sortowania/grupowania przekopiujemy wartości z tych pól do pól o typie &#8222;lowercase&#8221;.<br />
Pola, do których będziemy kopiować wartości marek oraz modeli samochodów, a które to będą wykorzystywane do sortowania/grupowania po tych polach:
</p>
<pre class="brush:xml">&lt;field name="make_sort" type="lowercase" indexed="true" stored="false" /&gt;
&lt;field name="model_sort" type="lowercase" indexed="true" stored="false" /&gt;</pre>
<p>Pole zbiorcze, do którego będą kopiowane wartości z pól, po których chcemy wyszukiwać. Jako że do tego pola kopiujemy wartości z więcej niż jednego pola, musimy ustawić wartość atrybutu „multiValued” na true:
</p>
<pre class="brush:xml">&lt;field name="content" type="text" indexed="true" stored="false" multiValued="true"/&gt;</pre>
<p>Rok produkcji:
</p>
<pre class="brush:xml">&lt;field name="year" type="tint" indexed="true" stored="true" required="true" /&gt;</pre>
<p>Cena:
</p>
<pre class="brush:xml">&lt;field name="price" type="tfloat" indexed="true" stored="true" /&gt;</pre>
<p>Pojemność:
</p>
<pre class="brush:xml">&lt;field name="engine_size" type="tint" indexed="true" stored="true" /&gt;</pre>
<p>Przebieg:
</p>
<pre class="brush:xml">&lt;field name="mileage" type="tint" indexed="true" stored="true" /&gt;</pre>
<p>Kolor:
</p>
<pre class="brush:xml">&lt;field name="colour" type="lowercase" indexed="true" stored="true" /&gt;</pre>
<p>Pamiętamy o wartości false dla atrybutu „indexed” dla pola „Czy uszkodzony”:
</p>
<pre class="brush:xml">&lt;field name="damaged" type="boolean" indexed="false" stored="true" /&gt;</pre>
<p>Zostało nam przekopiować wartości z pól, po których wyszukujemy do jednego pola zbiorczego:
</p>
<pre class="brush:xml">&lt;copyField source="make" dest="content"/&gt;
&lt;copyField source="model" dest="content"/&gt;
&lt;copyField source="year" dest="content"/&gt;</pre>
<p>&#8230; i ponownie pola marki i modelu do pól, po których będziemy sortować:
</p>
<pre class="brush:xml">&lt;copyField source="make" dest="make_sort"/&gt;
&lt;copyField source="model" dest="model_sort"/&gt;</pre>
<h2>Czy coś jeszcze do schemy?</h2>
<p>Uzupełnijmy scheme jeszcze o 3 elementy:<br />
Klucz unikalny dokumentu
</p>
<pre class="brush:xml">&lt;uniqueKey&gt;id&lt;/uniqueKey&gt;</pre>
<p>Domyślne pole, po którym wyszukujemy
</p>
<pre class="brush:xml">&lt;defaultSearchField&gt;content&lt;/defaultSearchField&gt;</pre>
<p>Domyślny operator, wykorzystywany przez parser zapytań do solr. Ustawmy go na wartość &#8222;AND&#8221;.
</p>
<pre class="brush:xml">&lt;solrQueryParser defaultOperator="AND"/&gt;</pre>
<p>Mamy zatem gotowy plik konfiguracyjny schema.xml! Zobaczmy jak wygląda w całej okazałości:
</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;schema name="carsale" version="1.2"&gt;
  &lt;types&gt;
   &lt;fieldType name="string" sortMissingLast="true" omitNorms="true"/&gt;
   &lt;fieldType name="boolean" sortMissingLast="true" omitNorms="true"/&gt;
   &lt;fieldType name="tint" precisionStep="8" omitNorms="true" positionIncrementGap="0"/&gt;
   &lt;fieldType name="tfloat" precisionStep="8" omitNorms="true" positionIncrementGap="0"/&gt;
   &lt;fieldType name="text" positionIncrementGap="100"&gt;
     &lt;analyzer&gt;
       &lt;tokenizer class="solr.WhitespaceTokenizerFactory"/&gt;
       &lt;filter class="solr.LowerCaseFilterFactory"/&gt;
     &lt;/analyzer&gt;
   &lt;/fieldType&gt;
   &lt;fieldType name="lowercase" positionIncrementGap="100"&gt;
     &lt;analyzer&gt;
       &lt;tokenizer class="solr.KeywordTokenizerFactory"/&gt;
       &lt;filter class="solr.LowerCaseFilterFactory" /&gt;
       &lt;filter class="solr.TrimFilterFactory" /&gt;
     &lt;/analyzer&gt;
   &lt;/fieldType&gt;
 &lt;/types&gt;
 &lt;fields&gt;
   &lt;field name="id" type="string" indexed="true" stored="true" required="true" /&gt;
   &lt;field name="make" type="text" indexed="false" stored="true" required="true" /&gt;
   &lt;field name="model" type="text" indexed="false" stored="true" required="true" /&gt;
   &lt;field name="make_sort" type="lowercase" indexed="true" stored="false" /&gt;
   &lt;field name="model_sort" type="lowercase" indexed="true" stored="false" /&gt;
   &lt;field name="year" type="tint" indexed="true" stored="true" required="true" /&gt;
   &lt;field name="price" type="tfloat" indexed="true" stored="true" /&gt;
   &lt;field name="engine_size" type="tint" indexed="true" stored="true" /&gt;
   &lt;field name="mileage" type="tint" indexed="true" stored="true" /&gt;
   &lt;field name="colour" type="lowercase" indexed="true" stored="true" /&gt;
   &lt;field name="damaged" type="boolean" indexed="false" stored="true" /&gt;
   &lt;field name="content" type="text" indexed="true" stored="false" multiValued="true"/&gt;
 &lt;/fields&gt;
 &lt;uniqueKey&gt;id&lt;/uniqueKey&gt;
 &lt;defaultSearchField&gt;content&lt;/defaultSearchField&gt;
 &lt;solrQueryParser defaultOperator="AND"/&gt;
 &lt;copyField source="make" dest="content"/&gt;
 &lt;copyField source="model" dest="content"/&gt;
 &lt;copyField source="make" dest="make_sort"/&gt;
 &lt;copyField source="model" dest="model_sort"/&gt;
 &lt;copyField source="year" dest="content"/&gt;
&lt;/schema&gt;</pre>
<h2>Podsumowując</h2>
<p>W dzisiejszym wpisie udało nam się stworzyć plik schema.xml, który pomoże nam tak zaindeksować dane, abyśmy mogli zrealizować funkcjonalności wyszukiwania ogłoszeń naszego serwisu sprzedaży samochodów. Chcielibyśmy jednak rozwijać nasz serwis, co będzie się wiązało z dodatkowymi zmianami w pliku konfiguracyjnym &#8230; i nie tylko. W następnych artykułach z cyklu &#8222;sprzedaż samochodów&#8221; będziemy realizować nowe wymagania oraz wprowadzać kolejne modyfikacje.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/2011/01/31/aplikacja-sprzedaz-samochodow-projektowanie-schema-xml-dla-naszych-potrzeb-cz-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
