Elasticsearch - previous / next functionality

I created a search engine to search all documents in my elasticsearch index. When a user clicks on a document on the search results page, he leaves the current page and opens the page for deleting this document.

Now id wanted to implement a little document navigation on this details page, but I cannot figure out how to create something like this using elasticsearch. I like having the previous document and the following document link on top of this document detail page.

My idea was to save all returned documents in a session cookie or something else to remember the next and previous document of this current search. But I also have pagination on this page of search results. When the user selects the last document on the results page, the following link will not work, because my current search has not yet received any documents.

Is this a common problem or specific? Have any of you come up with an idea that could help me solve this problem? Perhaps a scroll-API ?

thanks

+6
source share
2 answers

The following works beautifully for me. Make sure you use a regularly formatted list of sort definitions as follows:

 function getSortDefinitions() { return [ 'newest' => [ [ 'created_at' => 'desc' ], [ 'id' => 'desc' ], ], 'oldest' => [ [ 'created_at' => 'asc' ], [ 'id' => 'asc' ], ] 'highest' => [ [ 'price' => 'desc' ], [ 'created_at' => 'desc' ], [ 'id' => 'desc' ], ], 'lowest' => [ [ 'price' => 'asc' ], [ 'created_at' => 'asc' ], [ 'id' => 'asc' ], ], ]; } 

Aside: adding id causes the result set to have predictable ordering for records with the same timestamp. This often happens with testing instruments in which all records are stored at the same time.

Now, when someone is looking, they usually select several filters, perhaps a query and definitely a sort order. Create a table that stores this so you can create a search context to work with:

 create table search_contexts ( id int primary, hash varchar(255) not null, query varchar(255) not null, filters json not null, sort varchar(255) not null, unique search_contexts_hash_uk (hash) ); 

To select and get a link to a search context, use the following in your language, for example:

 function saveSearchContext($query, $filters, $sort) { // Assuming some magic re: JSON encoding of $filters $hash = md5(json_encode(compact('query', 'filters', 'sort'))); return SearchContext::firstOrCreate(compact('hash', 'query', 'filters', 'sort')); } 

Please note that we only insert the search context if it is no longer with the same parameters. Thus, we get one unique row for each search. You can choose to be overloaded with a volume and save one per search. If you decide to do this, use uniqid instead of md5 and just create a record.

On the results index page, whenever you create a link to the details page, use the hash as the query parameter, for example:

 http://example.com/details/2456?search=7ddf32e17a6ac5ce04a8ecbf782ca509 

In your detail page code, do the following:

 function getAdjacentDocument($search, $documentId, $next = true) { $sortDefinitions = getSortDefinitions(); if (!$next) { // Reverse the sort definitions by looping through $sortDefinitions // and swapping asc and desc around $sortDefinitions = array_map($sortDefinitions, function ($defn) { return array_map($defn, function ($array) { $field = head(array_keys($array)); $direction = $array[$field]; $direction = $direction == 'asc' ? 'desc' : 'asc'; return [ $field => $direction ]; }); }); } // Add a must_not filter which will ensure that the // current page document ID is *not* in the results. $filters['blacklist'] = $documentId; $params = [ 'body' => [ 'query' => generateQuery($search->query, $filters), 'sort' => $sortDefinitions[$sort], // We are only interested in 1 document adjacent // to this one, limit results 'size' => 1 ] ]; $response = Elasticsearch::search($params); if ($response['found']) { return $response['hits']['hits'][0]; } } function getNextDocument($search, $documentId) { return getAdjacentDocument($search, $documentId, true); } function getPreviousDocument($search, $documentId) { return getAdjacentDocument($search, $documentId, false); } // Retrieve the search context given it hash as query parameter $searchContext = SearchContext::whereHash(Input::query('search'))->first(); // From the route segment $documentId = Input::route('id'); $currentDocument = Elasticsearch::get([ 'id' => $documentId, 'index' => 'documents' ]); $previousDocument = getPreviousDocument($searchContext, $documentId); $nextDocument = getNextDocument($searchContext, $documentId); 

The key to this method is that you generate two requests in addition to get for a detailed record.

One search goes forward from this record, the other backwards from this record, given the same search context in both cases, so they work according to eachother.

In both cases, you take the first record, which is not our current record, and it must be correct.

+1
source

If your documents use serial _id, you can simply make the current document _id + 1 and request it again.

-1
source

Source: https://habr.com/ru/post/981291/


All Articles