我刚刚为多租户网络应用设计了ElasticSearch映射。在这 应用程序,有站点ID:s和页面ID:s。页面ID:s 每个站点唯一,并随机生成。页面可以有子页面。
什么是最好的:
1)使用网站+网页ID的复合键:s?像这样:
"sitePageIdPath": "(siteID):(grandparent-page-ID).(parent-page-ID).(page-ID)"
或:
2)为网站ID和网页ID使用单独的字段?像这样:
"siteId": "(siteID)",
"pageIdPath": "(grandparent-page-ID).(parent-page-ID).(page-ID)"
?
我在想如果我将网站ID和页面ID合并到一个单独的字段中,那么ElasticSearch将需要仅处理 该字段,这应该比使用更高效。两个字段 - 在索引和搜索时都是如此?并且需要更少的存储空间。
然而,也许有一些我不知道的缺点?因此这个问题。
一些细节:1)我正在使用单个索引,而且我正在分配分片(100个分片),正如使用the "users" data flow pattern时所建议的那样。 2)我在URL中明确指定路由参数(即&routing=site-ID
),
不是通过索引文档中的任何 siteId 字段。
7小时后更新:
1)所有查询都应按网站ID(即租户ID)进行过滤。如果我将站点ID与页面ID结合起来,我想/希望我可以使用前缀过滤器来过滤站点ID。我想知道这是否与在单个专用 siteId 字段上过滤一样快(例如,可以缓存结果)。
2)示例查询:全文搜索。列出所有用户。列出所有页面。列出某个页面的所有子页面/后继页面。加载单个页面(通过 _source )。
22小时后更新:
3)我能够按页面ID进行搜索,因为作为ElasticSearch的{{1}},我存储了_id
。因此,将页面ID“隐藏”为 pageIdPath 的最后一个元素并不是一个问题。 我之前可能应该提到我有一个单独的页面ID字段,但我认为让问题保持简短。
4)我对这些ID字段使用(site-ID):(page-ID)
。
答案 0 :(得分:3)
如果您使用1个字段,则在编制索引和搜索时会出现性能问题。我认为你错误地认为1提交会加快速度。
如果使用1个字段,则基本上有2个映射选项:
如果您使用默认映射,字符串(siteID):(grandparent-page-ID).(parent-page-ID).(page-ID)
将被分析器分解为令牌(siteID)
(grandparent-page-ID)
(parent-page-ID)
(page-ID)
。现在你的id就像一个单词,当你想要匹配siteID时,术语或前缀过滤器可能会从pageID中找到匹配项。
如果您设置自己的分析器(我想知道您是否能想到这样做的好方法),首先想到的是关键字(或not_analyzed)分析器。这会将字符串保留为一个标记,这样您就不会丢失上下文。 然而现在使用前缀过滤器时会有很大的性能损失。想象一下,我将字符串"123.456.789"
索引为一个标记(siteID,parentpageID.pageID)。我想通过sideID = 123进行文件管理,因此我使用前缀过滤器。 As you can read here此前缀过滤器实际上已经被显示为bool
个查询,其中包含所有ORed的数百个术语(123
或1231
或1232
或1233
等等...),当您可以更好地构建数据时,这会大量浪费计算能力。
我建议您阅读有关lucene的PrefixQuery及其工作原理的更多信息。
如果我是你,我会这样做。
"properties": {
"site_id": {
"type": "string",
"index": "not_analyzed" //keyword would also work here, they are basically the same
},
"parent_page_id": {
"type": "string",
"index": "not_analyzed"
},
"page_id": {
"type": "string",
"index": "not_analyzed"
}<
"page_content": {
"type": "string",
"index": "standard" //you may want to use snowball to enable stemming
}
}
文本搜索&#34; elasticsearch教程&#34;在siteID&#34; 123&#34;
下"filtered": {
"query": {
"match": {
"page_content": "elasticsearch tutorial"
}
},
"filter": {
"term": {
"site_id": "123"
}
}
}
页面的所有子页面&#34; 456&#34;在网站&#34; 123&#34;
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"and": [
{
"term": {
"site_id": "123"
}
},
{
"term": {
"parent_page_id": "456"
}
}
}
}
答案 1 :(得分:0)
编辑: 此答案存在问题,即可能BooleanQuery.TooManyClauses exceptions
;在原始答案之后,请参阅下面的更新。的 /修改 的
我认为可以将网站ID和网页ID结合起来,并在查询时使用[与网站ID匹配的前缀过滤器]。我在the Query DSL docs中找到了这个信息:
某些过滤器已经产生易于缓存的结果,并且 缓存和不缓存它们之间的区别是行为 是否将结果放在缓存中。这些过滤器包含 术语,术语,前缀和范围过滤器
因此,结合网站ID和页面ID应该没关系w.r.t.表现我认为。我无法想到任何其他问题(请记住,按页面ID查找只是没有意义,因为如果没有网站ID,页面ID就没有任何意义。)
<强>更新强>
我猜 downvote 主要是 1)因为如果我将(Site-ID):(Parent-page-ID):(Page-ID)
合并到一个字段中,然后尝试搜索,则存在性能问题页面ID。但是,_id
字段中的页面ID可用:(site-ID):(page-ID)
,因此这不应该是一个问题。 (也就是说,我不只使用1个字段 - 我使用的是2个字段。)
对应于Ramseykhalaf查询的查询将是:
"filtered": {
"query": {
"match": {
"page_content": "search phrase"
}
},
"filter" : {
"prefix" : {
"_id" : "123:" // site ID is "123"
}
}
}
和
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"and": [{
"prefix" : {
"_id" : "123:" // site ID is "123"
}, {
"prefix": {
"pageIdPath": "456:789:" // section and sub section IDs are 456:789
// (I think I'd never search for a *subsection* only,
// without also knowing the parent section ID)
}
}]
}
}
(我将 sitePageIdPath 重命名为 pageIdPath ,因为网站ID存储在 _id 中)
另一个2)downvote 的次要原因可能是(直到现在我才知道这一点)前缀查询被分解为与所有条款匹配的布尔查询与指定的前缀,在我的情况下,这些布尔查询可能包含非常多的术语,如果相关网站中确实有很多页面(可能有)或部分ID(没有)。那么直接使用术语查询会更快吗?并且不能导致太多子句异常(请参阅下面的链接)。
有关PrefixQuery的更多信息,请参阅:
How to improve a single character PrefixQuery performance?和
With Lucene: Why do I get a Too Many Clauses error if I do a prefix search?
此布尔查询转换显然不仅适用于前缀查询,也适用于范围查询,请参阅例如Help needed figuring out reason for maxClauseCount is set to 1024 error和the Lucene BooleanQuery.TooManyClauses docs:“当尝试添加多于BooleanQuery.getMaxClauseCount()子句时抛出。如果 PrefixQuery,通常发生,在搜索“
期间,FuzzyQuery,WildcardQuery或TermRangeQuery被扩展为多个术语