Elasticsearch - 上一个/下一个功能
我创建了一个搜索引擎来搜索我的 elasticsearch 索引中的所有文档.当用户点击搜索引擎结果页面上的文档时,他会离开当前页面并打开该文档的详细页面.
I created a search engine to search all documents in my elasticsearch index. When a user hits a document on the searchengine resultpage he leaves the current page and opens the deatailpage of that document.
现在我想在那个详细信息页面上实现一个小的文档导航,但我不知道如何用 elasticsearch 创建这样的东西.我想在该文档详细信息页面的顶部有一个上一个文档和一个下一个文档链接.
Now id like to implement a little document navigation on that detail page but i cant figure out how to create something like this with elasticsearch. Id like to have a previous document and a next document link on top of that document detail page.
我的想法是将所有返回的文档保存在会话 cookie 或其他内容中,以记住当前搜索中的下一个和上一个文档.但我在那个搜索引擎结果页面上也有一个分页.当用户选择结果页面上的最后一个文档时,下一个链接将不起作用,因为我当前的搜索没有获得更多文档.
My idea was to save all returned documents in a session cookie or something else to remember the next and the previous document on that current search. But i also have a pagination on that searchengine resultpage. When a user selects the last document on a resultpage the next link wouldnt work because my current search hasnt got any more documents.
这是常见问题还是特定问题?你们中有人有什么想法可以帮助我解决这个问题吗?也许 scroll-API
?
Is this a common problem or to specific? Anybody of you got an idea which could help me to solve that problem?
Maybe the scroll-API
?
谢谢
推荐答案
以下对我来说效果很好.确保您使用的是常规格式的 sort
定义列表,如下所示:
The following works beautifully for me. Ensure that you are using a regularly formatted list of sort
definitions like this:
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' ],
],
];
}
旁白:添加 id
使结果集对具有相同时间戳的记录具有可预测的排序.这种情况经常发生在同时保存所有记录的测试装置中.
An aside: Adding id
makes the resultset have predictable ordering for records with the same timestamp. This happens often with testing fixtures where the records are all saved at the same time.
现在每当有人搜索时,他们通常会选择一些过滤器,可能是查询,当然还有排序顺序.创建一个存储它的表,以便您可以生成要使用的搜索上下文:
Now whenever someone searches, they have usually selected a few filters, perhaps a query and definitely a sort order. Create a table that stores this so you can generate 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)
);
在您选择的语言中使用类似以下内容来插入并获取对搜索上下文的引用:
Use something like the following in your language of choice to insert and get a reference to the search context:
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'));
}
请注意,我们仅在没有具有相同参数的搜索上下文时才插入搜索上下文.所以我们每次搜索都会得到一个唯一的行.您可以选择被数量淹没并在每次搜索时保存一个.如果您选择这样做,请使用 uniqid
而不是 md5
并创建记录.
Notice that we only insert a search context if there isn't one already there with the same parameters. So we end up with one unique row per search. You may choose to be overwhelmed by the volume and save one per search. If you choose to do that, use uniqid
instead of md5
and just create the record.
在结果索引页面上,无论何时生成详细信息页面的链接,都使用哈希作为查询参数,如下所示:
On the results index page, whenever you generate a link to the detail page, use the hash as a query parameter like this:
http://example.com/details/2456?search=7ddf32e17a6ac5ce04a8ecbf782ca509
在您的详细信息页面代码中,执行以下操作:
In your detail page code, do something like this:
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's 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's 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);
这项技术的关键是除了生成两个搜索详细记录的 get
.
The key to this technique is that you are generating two searches in addition to
the get
for the detail record.
一个搜索从那个记录向前,另一个从那个记录向后,在两种情况下都给定相同的搜索上下文,因此它们可以相互配合.
One search goes forwards from that record, the other goes backwards from that record, given the same search context in both cases so they work in line with eachother.
在这两种情况下,您都取不是我们当前记录的第一条记录,它应该正确.
In both cases, you take the first record that is not our current record, and it should be correct.
相关文章