nested初解

了解这个nested之前呢,需要先了解两个基本概念---索引时关联和查询时关联,另外还有es的两种类型,object类型和join。

Lucene中的关联

1. 查询时关联 Query-time join

简介

最直接的做法,类似于传统关系型数据库中用于处理多表连接的一种做法,为两张"表"建立一个外键关联,以此来解决关联问题,下面是Lucene对外提供的接口工具方法。当然不止这一个,还有其他的,这里我就不多列举了。

public final class JoinUtil {

/**

* A query time join using global ordinals over a dedicated join field.

*

* This join has certain restrictions and requirements:

* 1) A document can only refer to one other document. (but can be referred by one or more documents)

* 2) Documents on each side of the join must be distinguishable. Typically this can be done by adding an extra field

* that identifies the "from" and "to" side and then the fromQuery and toQuery must take the this into account.

* 3) There must be a single sorted doc values join field used by both the "from" and "to" documents. This join field

* should store the join values as UTF-8 strings.

* 4) An ordinal map must be provided that is created on top of the join field.

*

* Note: min and max filtering and the avg score mode will require this join to keep track of the number of times

* a document matches per join value. This will increase the per join cost in terms of execution time and memory.

*

* @param joinField The {@link SortedDocValues} field containing the join values

* @param fromQuery The query containing the actual user query. Also the fromQuery can only match "from" documents.

* @param toQuery The query identifying all documents on the "to" side.

* @param searcher The index searcher used to execute the from query

* @param scoreMode Instructs how scores from the fromQuery are mapped to the returned query

* @param ordinalMap The ordinal map constructed over the joinField. In case of a single segment index, no ordinal map

* needs to be provided.

* @param min Optionally the minimum number of "from" documents that are required to match for a "to" document

* to be a match. The min is inclusive. Setting min to 0 and max to Interger.MAX_VALUE

* disables the min and max "from" documents filtering

* @param max Optionally the maximum number of "from" documents that are allowed to match for a "to" document

* to be a match. The max is inclusive. Setting min to 0 and max to Interger.MAX_VALUE

* disables the min and max "from" documents filtering

* @return a {@link Query} instance that can be used to join documents based on the join field

* @throws IOException If I/O related errors occur

*/

public static Query createJoinQuery(String joinField,

Query fromQuery,

Query toQuery,

IndexSearcher searcher,

ScoreMode scoreMode,

OrdinalMap ordinalMap,

int min,

int max

}

查询过程

先执行fromQuery查询子文档,获得一个collector

然后再以这个collector的结果作为条件查询toQuery结果

总结

query-time join由于查询了两遍,性能会下降。

2. 索引时关联 Index-time join

简介

索引时关联,顾名思义,就是在建立索引时对两种文档进行关联。

关联的思路

lucene中的文档都是有顺序的,那么考虑一种最方便的做法,我们把关联的文档以一定的顺序写入,那么就能很快的找到关联的文档。比如目前的做法就是先索引子文档,再索引主文档那么在索引中,数据是这样存储的。

有以下三个doc,A,B,C,他们有子文档1,2......

A - 1,2,3

B - 4

C - 5,6

在索引中:1,2,3,A,4,B,5,6,C

当我们要找子文档是1的父文档时,只要找到1的位置,然后一直遍历,直到不是子文档的时候就会找到他的父文档。

Lucene用法

public class ToParentBlockJoinQuery extends Query {

/** Create a ToParentBlockJoinQuery.

*

* @param childQuery Query matching child documents.

* @param parentsFilter Filter identifying the parent documents.

* @param scoreMode How to aggregate multiple child scores

* into a single parent score.

**/

public ToParentBlockJoinQuery(Query childQuery, BitSetProducer parentsFilter, ScoreMode scoreMode)

}

上述Query就是lucene中用来进行查询关联的Query类,第一个Query是子文档的查询条件。

总结

这种方式比query time index要快一些,大概30%,目前更建议在合适的情况下选择两种不同的关联用法。

object

在Elasticsearch中,object对象其实是被当做多列数据来处理的。比如:

{

"group" : "fans",

"user" : [

{

"first" : "John",

"last" : "Smith"

},

{

"first" : "Alice",

"last" : "White"

}

]

}

这样一个JSON,如果将user定义为object类型,那么它会变为这样一个索引,与以下json生成同样的索引

{

"group" : "fans",

"user.first" : ["John","Alice"],

"user.last" : ["Smith","White"]

}

很明显,在搜索的时候,如果我们使用一个条件,同时满足firstname是"Alice"和lastname是"Smith"的时候,这条结果也会被返回。

因此,object不能用来作为父子关系的文档来进行索引。

nested

nested就是为了解决上述问题而制造的。nested实际上在索引中会创建一个父文档以及多个子文档(比如上述事例,数量取决于user数组的大小)。

nested搜索原理

查询

主要的查询是在NestedQueryBuilder.java这个类中,这里会构建一个ESToParentBlockJoinQuery对象,这个对象中实际上封装了一个ToParentBlockJoinQuery。ToParentBlockJoinQuery是Lucene中的一种查询,主要用于索引时关联的查询使用。

protected Query doToQuery(QueryShardContext context) throws IOException {

ObjectMapper nestedObjectMapper = context.getObjectMapper(path);

if (nestedObjectMapper == null) {

if (ignoreUnmapped) {

return new MatchNoDocsQuery();

} else {

throw new IllegalStateException("[" + NAME + "] failed to find nested object under path [" + path + "]");

}

}

if (!nestedObjectMapper.nested().isNested()) {

throw new IllegalStateException("[" + NAME + "] nested object under path [" + path + "] is not of nested type");

}

final BitSetProducer parentFilter;

Query innerQuery;

ObjectMapper objectMapper = context.nestedScope().getObjectMapper();

if (objectMapper == null) {

parentFilter = context.bitsetFilter(Queries.newNonNestedFilter(context.indexVersionCreated()));

} else {

parentFilter = context.bitsetFilter(objectMapper.nestedTypeFilter());

}

try {

context.nestedScope().nextLevel(nestedObjectMapper);

innerQuery = this.query.toQuery(context);

} finally {

context.nestedScope().previousLevel();

}

// ToParentBlockJoinQuery requires that the inner query only matches documents

// in its child space

if (new NestedHelper(context.getMapperService()).mightMatchNonNestedDocs(innerQuery, path)) {

innerQuery = Queries.filtered(innerQuery, nestedObjectMapper.nestedTypeFilter());

}

return new ESToParentBlockJoinQuery(innerQuery, parentFilter, scoreMode,

objectMapper == null ? null : objectMapper.fullPath());

}

聚合

Nested 实际的查询时一个聚合NestedAggregator,主要实现在NestedAggregator.java这个类中:

public LeafBucketCollector getLeafCollector(final LeafReaderContext ctx, final LeafBucketCollector sub) throws IOException {

IndexReaderContext topLevelContext = ReaderUtil.getTopLevelContext(ctx);

IndexSearcher searcher = new IndexSearcher(topLevelContext);

searcher.setQueryCache(null);

Weight weight = searcher.createWeight(searcher.rewrite(childFilter), ScoreMode.COMPLETE_NO_SCORES, 1f);

Scorer childDocsScorer = weight.scorer(ctx);

final BitSet parentDocs = parentFilter.getBitSet(ctx);

final DocIdSetIterator childDocs = childDocsScorer != null ? childDocsScorer.iterator() : null;

if (collectsFromSingleBucket) {

return new LeafBucketCollectorBase(sub, null) {

@Override

public void collect(int parentDoc, long bucket) throws IOException {

// if parentDoc is 0 then this means that this parent doesn't have child docs (b/c these appear always before the parent

// doc), so we can skip:

if (parentDoc == 0 || parentDocs == null || childDocs == null) {

return;

}

final int prevParentDoc = parentDocs.prevSetBit(parentDoc - 1);

int childDocId = childDocs.docID();

if (childDocId <= prevParentDoc) {

childDocId = childDocs.advance(prevParentDoc + 1);

}

for (; childDocId < parentDoc; childDocId = childDocs.nextDoc()) {

collectBucket(sub, childDocId, bucket);

}

}

};

} else {

return bufferingNestedLeafBucketCollector = new BufferingNestedLeafBucketCollector(sub, parentDocs, childDocs);

}

}

主要包括几部分:

先拿到父文档的docid集合。

获取父子文档的docid迭代器。

判断子文档是否符合条件,符合条件的数据放到collectBucket。

join

es中的join实际上就是query time join的实现,以一个字段作为关联的主键,然后进行关联查询,具体查询的实现逻辑如下,原理还是JoinUtil.createJoinQuery

public class HasChildQueryBuilder extends AbstractQueryBuilder {

public Query rewrite(IndexReader reader) throws IOException {

Query rewritten = super.rewrite(reader);

if (rewritten != this) {

return rewritten;

}

if (reader instanceof DirectoryReader) {

IndexSearcher indexSearcher = new IndexSearcher(reader);

indexSearcher.setQueryCache(null);

indexSearcher.setSimilarity(similarity);

IndexOrdinalsFieldData indexParentChildFieldData = fieldDataJoin.loadGlobal((DirectoryReader) reader);

OrdinalMap ordinalMap = indexParentChildFieldData.getOrdinalMap();

return JoinUtil.createJoinQuery(joinField, innerQuery, toQuery, indexSearcher, scoreMode,

ordinalMap, minChildren, maxChildren);

} else {

if (reader.leaves().isEmpty() && reader.numDocs() == 0) {

// asserting reader passes down a MultiReader during rewrite which makes this

// blow up since for this query to work we have to have a DirectoryReader otherwise

// we can't load global ordinals - for this to work we simply check if the reader has no leaves

// and rewrite to match nothing

return new MatchNoDocsQuery();

}

throw new IllegalStateException("can't load global ordinals for reader of type: " +

reader.getClass() + " must be a DirectoryReader");

}

}

}

java+es+nested_Elasticsearch中的关联查询。Nested类型介绍及查询原理。。相关推荐

  1. java有push方法么_[Java教程]js中push和join方法使用介绍

    [Java教程]js中push和join方法使用介绍 0 2013-10-09 07:00:17 push和join方法想必大家并不陌生吧,在本文将为大家详细介绍下js中的push和join方法的使用 ...

  2. Java之戳中痛点 - (4)i++ 和 ++i 探究原理

    先看一个例子: package com.test;public class AutoIncrement {public static void main(String[] args) {int a=7 ...

  3. java查询数据比Oracle少,java对ORACLE中的于NCHAR数据的处理,查询

    nchar 数据如果指定了长度,如果数据不满指定的位数,将会在后面补空格. 所以当你使用 jdbc 或者其他框架以该字段作为查询条件时,形成的 sql 也会自动补空格 如: SELECT FROM m ...

  4. java从学号中提取班级_如何实现表间查询并提取班级号

    今天下午在整理学生模块成绩时,突然发现学生的模块成绩表中的班级是教学班,而发展报告是要求按行政班进行填写的.就有个问题了:如何按学籍号(因为发展报告只需要用学籍的学生填写),而现在就要把模块成绩表中的 ...

  5. java调用wadl_Java中的WADL:温和的介绍

    java调用wadl WADL( Web应用程序描述语言 )对REST而言,WSDL对SOAP而言. 这种语言的存在引起了很多争议(请参阅: 我们需要WADL吗? 或者 需要 WADL还是不需要WAD ...

  6. java中什么时候应用异常_生产Java应用程序中的十大异常类型-基于1B事件

    java中什么时候应用异常 Pareto记录原理:97%的记录错误语句是由3%的唯一错误引起的 在最新的数据整理帖子之后,我们收到了很多反馈和问题,我们发现97%的记录错误是由10个唯一错误引起的 . ...

  7. 生产Java应用程序中的十大异常类型-基于1B事件

    Pareto记录原理:97%的记录错误语句是由3%的唯一错误引起的 在最新的数据整理帖子之后,我们收到了很多反馈和问题,在该文章中,我们显示97%的记录错误是由10个唯一错误引起的 . 根据普遍的需求 ...

  8. java选择语句中switch的用法(详细介绍)

    一.什么时候用switch? 在java中控制流程语句是由选择语句.循环语句.跳转语句构成.选择语句包括 if 和 switch,在过多的使用 if 语句嵌套会使程序很难阅读,这时利用 switch ...

  9. java教程 invoke_Java中Method的Invoke方法详细介绍

    这篇文章主要介绍了详解Java中Method的Invoke方法,需要的朋友可以参考下 在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而 ...

  10. Java web应用中如何判断Web容器类型

    问题背景:最近在开发一个人行的项目,由于不同的商行所使用的WEB容器不一样,导致同样的代码在不同的容器中运行的效果不一样.因此想在代码中添加容器判断,从而让应用自动选择不同的实现. 问题描述:开发阶段 ...

最新文章

  1. 精灵沿着正方形路线运动暂停2秒后然后再将自己放大4倍
  2. 在装好的xp系统里面如何添加新的硬件设备
  3. 《四世同堂》金句摘抄(十八)
  4. java程序调用Oracle 存储过程 获取返回值(无返回,非结果集,结果集)
  5. qt的一些参数配置 win和linux
  6. DVB开发之OTA升级
  7. 【echarts】 tooltip显示图片
  8. Entity framework 配置文件,实现类,测试类
  9. C语言实现舒尔特表格生成器
  10. 人力资源管理系统需求分析说明书
  11. 统计报表可视化html,用统计图让数据可视化
  12. MATLAB创建三对角线矩阵-211103
  13. win7系统安装卡在启动服务器,安装win7卡在启动界面不动进不了BIOS的解决方法 - 系统家园...
  14. 干货!必看创意按钮设计,打造真正的按钮诱惑
  15. python量化交易书籍推荐知乎_GitHub - XingkaiLiang/vnpy: 基于python的开源量化交易平台开发框架...
  16. Sixth season fifteenth and sixteenth episode,things that could have been......(没有如果)
  17. (附源码)计算机毕业设计ssm城市智能公交系统
  18. 用程序员计算机算进制,一文带你读懂计算机进制
  19. CSS中fixed和absolute区别
  20. 概率论与数理统计(知识点概览)

热门文章

  1. vue实例的watch选项
  2. 关于 nor it's doXxx() equivalent is defined in action class 的问题解决办法
  3. JQuery 动态设置setInterval定时器时间间隔
  4. VUCA的复杂性——业务架构真正的挑战
  5. 【龙芯1c库】封装gpio接口和使用示例
  6. 今日恐慌与贪婪指数为15 恐慌程度小幅上升
  7. MySQL大数据量查询方案
  8. 关于查看nginx的访问量的部分总结
  9. 【DDR3_Electrical Characteristics and AC Timing】_Data Setup,Hold and Slew Rate Derating
  10. USYD悉尼大学DATA 2002 【R语言学习2】在 Tidyverse 中与数据通信 (Communicating with Data in the Tidyverse)