<?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>deep paging &#8211; Solr.pl</title>
	<atom:link href="https://solr.pl/en/tag/deep-paging/feed/" rel="self" type="application/rss+xml" />
	<link>https://solr.pl/en/</link>
	<description>All things to be found - Blog related to Apache Solr &#38; Lucene projects - https://solr.apache.org</description>
	<lastBuildDate>Thu, 12 Nov 2020 14:17:07 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>
	<item>
		<title>Solr 4.7 &#8211; efficient deep paging</title>
		<link>https://solr.pl/en/2014/03/10/solr-4-7-efficient-deep-paging/</link>
					<comments>https://solr.pl/en/2014/03/10/solr-4-7-efficient-deep-paging/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Kuć]]></dc:creator>
		<pubDate>Mon, 10 Mar 2014 14:16:29 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<category><![CDATA[cursor]]></category>
		<category><![CDATA[deep paging]]></category>
		<category><![CDATA[solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=626</guid>

					<description><![CDATA[Long, long time ago, we described a problem called deep paging. To keep things short &#8211; the deeper you want to go in the results, the slower the query will be. This is because Solr needs to prepare the data]]></description>
										<content:encoded><![CDATA[<p>Long, long time ago, we described a problem called <a href="http://solr.pl/en/2011/07/18/deep-paging-problem/" target="_blank" rel="noopener noreferrer">deep paging</a>. To keep things short &#8211; the deeper you want to go in the results, the slower the query will be. This is because Solr needs to prepare the data from the beginning for each query. Until Solr 4.7 there wasn&#8217;t a good solution for that problem. With the recently released Solr version, we got a possibility of using so called cursor to drastically improve performance of deep paging.</p>
<p><span id="more-626"></span></p>
<h2>The problem</h2>
<p>The deep paging problem is quite easy to define. To return search results Solr must prepare an in-memory structure and return part of it. Returning the part of the structure is simple, if that part comes from the beginning of the structure. However, if we want to return page number 10.000 (where we return 20 results per page) Solr needs to prepare a structure containing minimum of 200.000 elements (10.000 * 20). You see that it not only takes time, but also memory.</p>
<p>The good thing is, that with the release of Solr 4.7 the situation had changed &#8211; the cursor has been introduced. Cursor is a logic structure, that doesn&#8217;t require its state to be stored on the server side. Cursor contains information about storing and lest document returned in the results. Because of that, Solr doesn&#8217;t need to start search from beginning each time we want to get next page of results. It results in drastic performance improvement when using cursor and going deep into results.</p>
<h2>Usage</h2>
<p>Cursor usage is very simple. To tell Solr to return cursor, in the first query we need to pass an additional parameter &#8211; <em>cursorMark=*</em>. In result, apart from documents, we will get a cursor identifier returned in the <em>nextCursorMark</em> parameter. Let&#8217;s look at the example.</p>
<h3>The query</h3>
<p>Let&#8217;s start with a very simple query:
</p>
<pre class="brush:bash">curl 'localhost:8983/solr/select?q=*:*&amp;rows=1&amp;sort=score+desc,id+asc&amp;cursorMark=*'</pre>
<p>There are four things here that we are interested in. First of off, we either omit the <em>start</em> parameter or we set it to <em>0</em>. The <em>rows</em> parameter can take values we need, there is no limitation on it. Of course, we passed the <em>cursorMark=*</em> parameter, to tell Solr that we want the cursor to be used. The final thing we did is sorting definition. We need to define sorting for cursor to be working, one that will tell cursor how to behave. That&#8217;s why we needed to overwrite default sorting and include sorting not only by score, by also by document identifier.</p>
<h3>Search results</h3>
<p>Our query returns the following search results:
</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;response&gt;
 &lt;lst name="responseHeader"&gt;
  &lt;int name="status"&gt;0&lt;/int&gt;
  &lt;int name="QTime"&gt;33&lt;/int&gt;
  &lt;lst name="params"&gt;
   &lt;str name="sort"&gt;score desc,id asc&lt;/str&gt;
   &lt;str name="start"&gt;0&lt;/str&gt;
   &lt;str name="q"&gt;*:*&lt;/str&gt;
   &lt;str name="cursorMark"&gt;*&lt;/str&gt;
   &lt;str name="rows"&gt;1&lt;/str&gt;
  &lt;/lst&gt;
 &lt;/lst&gt;
&lt;result name="response" numFound="32" start="0"&gt;
 &lt;doc&gt;
  &lt;str name="id"&gt;0579B002&lt;/str&gt;
  &lt;str name="name"&gt;Canon PIXMA MP500 All-In-One Photo Printer&lt;/str&gt;
  &lt;str name="manu"&gt;Canon Inc.&lt;/str&gt;
  &lt;str name="manu_id_s"&gt;canon&lt;/str&gt;
  &lt;arr name="cat"&gt;
   &lt;str&gt;electronics&lt;/str&gt;
   &lt;str&gt;multifunction printer&lt;/str&gt;
   &lt;str&gt;printer&lt;/str&gt;
   &lt;str&gt;scanner&lt;/str&gt;
   &lt;str&gt;copier&lt;/str&gt;
  &lt;/arr&gt;
  &lt;arr name="features"&gt;
   &lt;str&gt;Multifunction ink-jet color photo printer&lt;/str&gt;
   &lt;str&gt;Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi&lt;/str&gt;
   &lt;str&gt;2.5" color LCD preview screen&lt;/str&gt;
   &lt;str&gt;Duplex Copying&lt;/str&gt;
   &lt;str&gt;Printing speed up to 29ppm black, 19ppm color&lt;/str&gt;
   &lt;str&gt;Hi-Speed USB&lt;/str&gt;
   &lt;str&gt;memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard&lt;/str&gt;
  &lt;/arr&gt;
  &lt;float name="weight"&gt;352.0&lt;/float&gt;
  &lt;float name="price"&gt;179.99&lt;/float&gt;
  &lt;str name="price_c"&gt;179.99,USD&lt;/str&gt;
  &lt;int name="popularity"&gt;6&lt;/int&gt;
  &lt;bool name="inStock"&gt;true&lt;/bool&gt;
  &lt;str name="store"&gt;45.19214,-93.89941&lt;/str&gt;
  &lt;long name="_version_"&gt;1461375031699308544&lt;/long&gt;&lt;/doc&gt;
 &lt;/result&gt;
 &lt;str name="nextCursorMark"&gt;AoIIP4AAACgwNTc5QjAwMg==&lt;/str&gt;
&lt;/response&gt;</pre>
<p>As we can see, in addition to standard search results, we got the cursor identifier in the <em>nextCursorMark </em>section. Now, to get the next results bound to that cursor, we need to pass that identifier using the <em>cursorMark</em> parameter.</p>
<h3>Next query</h3>
<p>Our next query looks as follows (note the <em>cursorMark</em> parameter value):
</p>
<pre class="brush:bash">curl 'localhost:8983/solr/select?q=*:*&amp;rows=1&amp;sort=score+desc,id+asc&amp;cursorMark=AoIIP4AAACgwNTc5QjAwMg=='</pre>
<p>The results were as follows:
</p>
<pre class="brush:xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;response&gt;
 &lt;lst name="responseHeader"&gt;
  &lt;int name="status"&gt;0&lt;/int&gt;
  &lt;int name="QTime"&gt;2&lt;/int&gt;
  &lt;lst name="params"&gt;
   &lt;str name="sort"&gt;score desc,id asc&lt;/str&gt;
   &lt;str name="indent"&gt;true&lt;/str&gt;
   &lt;str name="q"&gt;*:*&lt;/str&gt;
   &lt;str name="cursorMark"&gt;AoIIP4AAACgwNTc5QjAwMg==&lt;/str&gt;
   &lt;str name="rows"&gt;1&lt;/str&gt;
  &lt;/lst&gt;
 &lt;/lst&gt;
&lt;result name="response" numFound="32" start="0"&gt;
 &lt;doc&gt;
  &lt;str name="id"&gt;100-435805&lt;/str&gt;
  &lt;str name="name"&gt;ATI Radeon X1900 XTX 512 MB PCIE Video Card&lt;/str&gt;
  &lt;str name="manu"&gt;ATI Technologies&lt;/str&gt;
  &lt;str name="manu_id_s"&gt;ati&lt;/str&gt;
  &lt;arr name="cat"&gt;
   &lt;str&gt;electronics&lt;/str&gt;
   &lt;str&gt;graphics card&lt;/str&gt;
  &lt;/arr&gt;
  &lt;arr name="features"&gt;
   &lt;str&gt;ATI RADEON X1900 GPU/VPU clocked at 650MHz&lt;/str&gt;
   &lt;str&gt;512MB GDDR3 SDRAM clocked at 1.55GHz&lt;/str&gt;
   &lt;str&gt;PCI Express x16&lt;/str&gt;
   &lt;str&gt;dual DVI, HDTV, svideo, composite out&lt;/str&gt;
   &lt;str&gt;OpenGL 2.0, DirectX 9.0&lt;/str&gt;
  &lt;/arr&gt;
  &lt;float name="weight"&gt;48.0&lt;/float&gt;
  &lt;float name="price"&gt;649.99&lt;/float&gt;
  &lt;str name="price_c"&gt;649.99,USD&lt;/str&gt;
  &lt;int name="popularity"&gt;7&lt;/int&gt;
  &lt;bool name="inStock"&gt;false&lt;/bool&gt;
  &lt;date name="manufacturedate_dt"&gt;2006-02-13T00:00:00Z&lt;/date&gt;
  &lt;str name="store"&gt;40.7143,-74.006&lt;/str&gt;
  &lt;long name="_version_"&gt;1461375031846109184&lt;/long&gt;&lt;/doc&gt;
 &lt;/result&gt;
 &lt;str name="nextCursorMark"&gt;AoIIP4AAACoxMDAtNDM1ODA1&lt;/str&gt;
&lt;/response&gt;</pre>
<p>As we can see, the returned <em>nextCursorMark</em> was different again.</p>
<h3>Further queries</h3>
<p>Logic for further queries is simple &#8211; we use the <em>cursorMark</em> parameter with the value returned with the previous search results. So again, our next query would look as follows:
</p>
<pre class="brush:bash">curl 'localhost:8983/solr/select?q=*:*&amp;rows=1&amp;sort=score+desc,id+asc&amp;cursorMark=AoIIP4AAACoxMDAtNDM1ODA1'</pre>
<h2>Summary</h2>
<p>Simple API and massive gain on performance in case of deep paging. That&#8217;s how I think the cursor introduced in Solr 4.7 could be summarized. I decided not do re-do performance tests, there are ones already done by Chris Hostetter in his entry about this functionality. If you are interested please look at: <a href="http://searchhub.org/2013/12/12/coming-soon-to-solr-efficient-cursor-based-iteration-of-large-result-sets/">http://searchhub.org/2013/12/12/coming-soon-to-solr-efficient-cursor-based-iteration-of-large-result-sets/</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/en/2014/03/10/solr-4-7-efficient-deep-paging/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Deep paging problem</title>
		<link>https://solr.pl/en/2011/07/18/deep-paging-problem/</link>
					<comments>https://solr.pl/en/2011/07/18/deep-paging-problem/#respond</comments>
		
		<dc:creator><![CDATA[Rafał Kuć]]></dc:creator>
		<pubDate>Mon, 18 Jul 2011 19:45:36 +0000</pubDate>
				<category><![CDATA[Solr]]></category>
		<category><![CDATA[deep]]></category>
		<category><![CDATA[deep paging]]></category>
		<category><![CDATA[index]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[paging]]></category>
		<category><![CDATA[solr]]></category>
		<guid isPermaLink="false">http://sematext.solr.pl/?p=359</guid>

					<description><![CDATA[Imagine the following problem &#8211; we have an application that expects Solr to return the results sorted on the basis of some field. Those results will be than paged in the GUI. However, if the person using the GUI application]]></description>
										<content:encoded><![CDATA[<p>Imagine the following problem &#8211; we have an application that expects Solr to return the results sorted on the basis of some field. Those results will be than paged in the GUI. However, if the person using the GUI application immediately selects the tenth, twentieth, or fiftieth page of search results there is a problem &#8211; the wait time. Is there anything we can do about this? Yes, we can help Solr a bit.</p>
<p><span id="more-359"></span></p>
<h3>A few numbers at the beginning</h3>
<p>Let&#8217;s start with the query and statistics. Imagine that we have the following query, which is sent to Solr to get the five hundredth page of search results:
</p>
<pre class="brush:xml">q=*:*&amp;sort=price+asc&amp;rows=100&amp;start=50000</pre>
<p>What Solr must do Solr to retrieve and render the results list ? Of course, read the documents from Lucene index. There is of course the question of how many documents to be read from the index? Is it 100? Unfortunately, no. Solr must collect 50.100 sorted documents from the Lucene index, due to the fact that we want 100 documents starting from 50.000-th. Kinda scary. Now let&#8217;s look at the comparison of how long it takes for Solr download the first page of search results (the query <code>q=*:*&amp;sort=price+asc&amp;rows=100&amp;start=0</code>) and how long it takes to render the last page of search results (ie the query <code>q=*:*&amp;sort = price+asc&amp;rows=100&amp;start=999900</code>). The test was performed on an index containing a million documents, consisting of four fields: <code>id</code> (<code>string</code>), <code>name</code> (<code>text</code>), <code>description</code> (<code>text</code>), <code>price</code> (<code>long</code>). Before every test iteration Solr was started the query was run and Solr was truned off. These steps were repeated 100 times for each of the queries, and times is seen in the table are the arithmetic mean of the query time execution. Here are the test results:</p>
[table “18” not found /]<br />

<h3>Typical solutions</h3>
<p>Of course we can try to set the cache or the size of <em>queryResultWindowSize</em>, but there will be a problem of how to set the size, there may be a situation where it will be insufficient or not relevant entry in the memory of Solr and then waiting time for the n-th search page will be very long. We can also try adding warming queries, but we won&#8217;t be able to prepare all the combinations, but even if we could the the cache would have to be big. So we won&#8217;t be able to achieve the desired results with any of these solutions.</p>
<h3>Filters, filters and filter again</h3>
<p>This behavior Solr (and other applications based on Lucene too) is caused by the queue of documents, called. priority queue, which in order to display the last page of results must download all documents matching the query and return the ones we want located in the desired page. Of course, in our case, if we want the first page of search results queue will have 100 entries. However, if we want the last page will have Solr search in the queue to put one million documents. Of course what I told is in big simplification.</p>
<p>The idea is to limit the number of documents Lucene must put in the queue. How to do it ? We will use filters to help us, so in Solr we will use the fq parameter. Using a filter will limit the number of search results. The ideal size of the queue would be the one that is passed in the rows parameter of query. However, this situation is ideal and not very possible in most situations. An additional problem is that asking a query with a filter we can not determine the number of results, because we do not know how much documents will the filter return. The solution to this problem is the making two queries instead of just one &#8211; the first one to see how fimiting is our filter thus using <code>rows=0</code> and <code>start=0</code>, and the second is already adequately calculated (example below).</p>
<p>The maximum price of the product in the test data is 10,000, and the minimum is 0. So to the first query we will add the following bracket: <code>&lt;0; 1000&gt;</code>, and to the second query, we add the following bracket: <code>&lt;9000; 10000&gt;</code>.</p>
<h3>Disadvantages of solution based on filters</h3>
<p>There is one minus the filter-based solution and it is quite large. It may happen that the number of results to which the filter is limiting the query is too small. What then? We should increase the choosen bracket for the filter. Of course we can calculate the optimal brackets on the basis of our data, but it depends on the data and queries and why I won&#8217;t be talking about this at this point.</p>
<h3>What is the performance after the change?</h3>
<p>So let&#8217;s repeat the tests, but now let&#8217;s implement the filter based approach. So the first will just return the first page of results (the query <code>q=*:*&amp;sort=price+asc&amp;rows=100&amp;start=0&amp;fq=price:[0+TO+1000]</code>). The second query (actually two queries) will will be used the check the number of results and then fetch those results (those two queries: <code>q=*:*&amp;sort=price+asc&amp;rows=0&amp;start=0&amp;fq=price:[9000+TO+10,000]</code> and <code>q=*:*&amp;sort=price+asc&amp;rows=100&amp;start=100037&amp;fq=price:[9000+TO+10000]</code>). It is worth noting about the changed start parameter in the query, due to the fact that we get fewer search results (this is caused by the fq parameter). This test was was carried out in similar way to the previous one &#8211; start Solr, run a query (or queries), and shutdown Solr. The number seen in the table are the arithmetic mean of query time execution.</p>
[table “19” not found /]<br />

<p>As you can see, the query performance changed. We can therefore conclude that we succeeded. Of course, we could be tempted by further optimizations, but for now let&#8217;s say that we are satisfied with the results. I suspect however that you can ask the following question:</p>
<h3>How is this handled by other search engines ?</h3>
<p>Good question, but the answer is trivial in total &#8211; one of the methods is to prevent a very deep paging. This method shall include Google. Try to type the word &#8220;ask&#8221; for the search and go further than 91 page of search results. Google didn&#8217;t let me <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3>Conclusions</h3>
<p>As you can see deep paging performance after our changes has increased significantly. We can now allow the user to search results without worrying that it will kill our Solr and the machine on which he works. Of course, this method is not without flaws, due to the fact that we need to know certain parameters (eg in our case, the maximum price for descending sort), but this is a solution that allows you to provide search results in a relatively low latency time when compared to the pre-optimization method.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://solr.pl/en/2011/07/18/deep-paging-problem/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
