├── .gitignore ├── 010_Intro ├── 00_README.md ├── 05_What_is_it.md ├── 10_Installing_ES.md ├── 15_API.md ├── 20_Document.md ├── 25_Tutorial_Indexing.md ├── 30_Tutorial_Search.md ├── 35_Tutorial_Aggregations.md ├── 40_Tutorial_Conclusion.md ├── 45_Distributed.md └── 50_Conclusion.md ├── 020_Distributed_Cluster ├── 00_Intro.md ├── 05_Empty_cluster.md ├── 10_Cluster_health.md ├── 15_Add_an_index.md ├── 20_Add_failover.md ├── 25_Scale_horizontally.md ├── 30_Scale_more.md └── 35_Coping_with_failure.md ├── 030_Data ├── 00_Intro.md ├── 05_Document.md ├── 10_Index.md ├── 15_Get.md ├── 20_Exists.md ├── 25_Update.md ├── 30_Create.md ├── 35_Delete.md ├── 40_Version_control.md ├── 45_Partial_update.md ├── 50_Mget.md ├── 55_Bulk.md └── 60_Conclusion.md ├── 040_Distributed_CRUD ├── 00_Intro.md ├── 05_Routing.md ├── 10_Shard_interaction.md ├── 15_Create_index_delete.md ├── 20_Retrieving.md ├── 25_Partial_updates.md ├── 30_Bulk_requests.md └── 35_Bulk_format.md ├── 050_Search ├── 00_Intro.md ├── 05_Empty_search.md ├── 10_Multi_index_multi_type.md ├── 15_Pagination.md └── 20_Query_string.md ├── 052_Mapping_Analysis ├── 00_Intro.md ├── 25_Data_type_differences.md ├── 30_Exact_vs_full_text.md ├── 35_Inverted_index.md ├── 40_Analysis.md ├── 45_Mapping.md └── 50_Complex_datatypes.md ├── 054_Query_DSL ├── 00_Intro.md ├── 55_Request_body_search.md ├── 60_Query_DSL.md ├── 65_Queries_vs_filters.md ├── 70_Important_clauses.md ├── 75_Queries_with_filters.md ├── 80_Validating_queries.md └── 85_Conclusion.md ├── 056_Sorting ├── 00_Intro.md ├── 85_Sorting.md ├── 88_String_sorting.md ├── 90_What_is_relevance.md └── 95_Fielddata.md ├── 060_Distributed_Search ├── 00_Intro.md ├── 05_Query_phase.md ├── 10_Fetch_phase.md ├── 15_Search_options.md └── 20_Scan_and_scroll.md ├── 070_Index_Mgmt ├── 00_Intro.md ├── 05_Create_Delete.md ├── 10_Settings.md ├── 15_Configure_Analyzer.md ├── 20_Custom_Analyzers.md ├── 25_Mappings.md ├── 30_Root_Object.md ├── 31_Metadata_source.md ├── 32_Metadata_all.md ├── 33_Metadata_ID.md ├── 35_Dynamic_Mapping.md ├── 40_Custom_Dynamic_Mapping.md ├── 45_Default_Mapping.md ├── 50_Reindexing.md └── 55_Aliases.md ├── 075_Inside_a_shard ├── 00_Intro.md ├── 20_Making_text_searchable.md ├── 30_Dynamic_indices.md ├── 40_Near_real_time.md ├── 50_Persistent_changes.md └── 60_Segment_merging.md ├── 080_Structured_Search ├── 00_structuredsearch.md ├── 05_term.md ├── 10_compoundfilters.md ├── 15_terms.md ├── 20_contains.md ├── 25_ranges.md ├── 30_existsmissing.md ├── 40_bitsets.md └── 45_filter_order.md ├── 100_Full_Text_Search ├── 00_Intro.md ├── 05_Match_query.md ├── 10_Multi_word_queries.md ├── 15_Combining_queries.md ├── 20_How_match_uses_bool.md ├── 25_Boosting_clauses.md ├── 30_Controlling_analysis.md └── 35_Relevance_is_broken.md ├── 110_Multi_Field_Search ├── 00_Intro.md ├── 05_Multiple_query_strings.md ├── 10_Single_query_string.md ├── 15_Best_field.md ├── 20_Tuning_best_field_queries.md ├── 25_Multi_match_query.md ├── 30_Most_fields.md ├── 35_Entity_search.md ├── 40_Field_centric.md ├── 45_Custom_all.md ├── 50_Cross_field.md └── 55_Not_analyzed.md ├── 120_Proximity_Matching ├── 00_Intro.md ├── 05_Phrase_matching.md ├── 10_Slop.md ├── 15_Multi_value_fields.md ├── 20_Scoring.md ├── 25_Relevance.md ├── 30_Performance.md └── 35_Shingles.md ├── 130_Partial_Matching ├── 00_Intro.md ├── 05_Postcodes.md ├── 10_Prefix_query.md ├── 15_WildcardRegexp.md ├── 20_Match_phrase_prefix.md ├── 25_Index_time.md ├── 30_Ngram_intro.md ├── 35_Search_as_you_type.md └── 40_Compound_words.md ├── 170_Relevance ├── 05_Intro.md ├── 10_Scoring_theory.md ├── 15_Practical_scoring.md ├── 20_Query_time_boosting.md ├── 25_Query_scoring.md ├── 30_Not_quite_not.md ├── 35_Ignoring_TFIDF.md ├── 40_Function_score_query.md ├── 45_Popularity.md ├── 50_Boosting_filtered_subsets.md ├── 55_Random_scoring.md ├── 60_Decay_functions.md ├── 65_Script_score.md ├── 70_Pluggable_similarities.md ├── 75_Changing_similarities.md └── 80_Conclusion.md ├── 200_Language_intro ├── 00_Intro.md ├── 10_Using.md ├── 20_Configuring.md ├── 30_Language_pitfalls.md ├── 40_One_language_per_doc.md ├── 50_One_language_per_field.md ├── 60_Mixed_language_fields.md └── 70_Conclusion.md ├── 210_Identifying_words ├── 00_Intro.md ├── 10_Standard_analyzer.md ├── 20_Standard_tokenizer.md ├── 30_ICU_plugin.md ├── 40_ICU_tokenizer.md └── 50_Tidying_text.md ├── 220_Token_normalization ├── 00_Intro.md ├── 10_Lowercasing.md ├── 20_Removing_diacritics.md ├── 30_Unicode_world.md ├── 40_Case_folding.md ├── 50_Character_folding.md └── 60_Sorting_and_collations.md ├── 230_Stemming ├── 00_Intro.md ├── 10_Algorithmic_stemmers.md ├── 20_Dictionary_stemmers.md ├── 30_Hunspell_stemmer.md ├── 40_Choosing_a_stemmer.md ├── 50_Controlling_stemming.md └── 60_Stemming_in_situ.md ├── 240_Stopwords ├── 10_Intro.md ├── 20_Using_stopwords.md ├── 30_Stopwords_and_performance.md ├── 40_Divide_and_conquer.md ├── 50_Phrase_queries.md ├── 60_Common_grams.md └── 70_Relevance.md ├── 260_Synonyms ├── 10_Intro.md ├── 20_Using_synonyms.md ├── 30_Synonym_formats.md ├── 40_Expand_contract.md ├── 50_Analysis_chain.md ├── 60_Multi_word_synonyms.md └── 70_Symbol_synonyms.md ├── 270_Fuzzy_matching ├── 10_Intro.md ├── 20_Fuzziness.md ├── 30_Fuzzy_query.md ├── 40_Fuzzy_match_query.md ├── 50_Scoring_fuzziness.md └── 60_Phonetic_matching.md ├── 300_Aggregations ├── 05_overview.md ├── 100_circuit_breaker_fd_settings.md ├── 105_filtering.md ├── 10_facets.md ├── 110_docvalues.md ├── 115_eager.md ├── 120_breadth_vs_depth.md ├── 125_Conclusion.md ├── 15_concepts_buckets.md ├── 20_basic_example.md ├── 21_add_metric.md ├── 22_nested_bucket.md ├── 23_extra_metrics.md ├── 28_bucket_metric_list.md ├── 30_histogram.md ├── 35_date_histogram.md ├── 40_scope.md ├── 45_filtering.md ├── 50_sorting_ordering.md ├── 55_approx_intro.md ├── 60_cardinality.md ├── 65_percentiles.md ├── 70_sigterms_intro.md ├── 75_sigterms.md ├── 90_fielddata.md └── 95_analyzed_vs_not.md ├── 310_Geopoints ├── 00_Intro.md ├── 20_Geopoints.md ├── 30_Filter_by_geopoint.md ├── 32_Bounding_box.md ├── 34_Geo_distance.md ├── 36_Caching_geofilters.md ├── 38_Reducing_memory.md └── 50_Sorting_by_distance.md ├── 320_Geohashes ├── 00_Intro.md ├── 40_Geohashes.md ├── 50_Geohash_mapping.md └── 60_Geohash_cell_filter.md ├── 330_Geo_aggs ├── 00_Intro.md ├── 60_Geo_aggs.md ├── 62_Geo_distance_agg.md ├── 64_Geohash_grid_agg.md └── 66_Geo_bounds_agg.md ├── 340_Geoshapes ├── 00_Intro.md ├── 70_Geoshapes.md ├── 72_Mapping_geo_shapes.md ├── 74_Indexing_geo_shapes.md ├── 76_Querying_geo_shapes.md ├── 78_Indexed_geo_shapes.md └── 80_Caching_geo_shapes.md ├── 400_Relationships ├── 10_Intro.md ├── 15_Application_joins.md ├── 20_Denormalization.md ├── 22_Top_hits.md ├── 25_Concurrency.md └── 26_Concurrency_solutions.md ├── 402_Nested ├── 00_Intro.md ├── 30_Nested_objects.md ├── 31_Nested_mapping.md ├── 32_Nested_query.md ├── 33_Nested_sorting.md └── 35_Nested_aggs.md ├── 404_Parent_Child ├── 00_Intro.md ├── 40_Parent_child.md ├── 45_Indexing_parent_child.md ├── 50_Has_child.md ├── 55_Has_parent.md ├── 60_Children_agg.md ├── 65_Grandparents.md └── 70_Practical_considerations.md ├── 410_Scaling ├── 10_Intro.md ├── 15_Shard.md ├── 20_Overallocation.md ├── 25_Kagillion_shards.md ├── 30_Capacity_planning.md ├── 35_Replica_shards.md ├── 40_Multiple_indices.md ├── 45_Index_per_timeframe.md ├── 50_Index_templates.md ├── 55_Retiring_data.md ├── 60_Index_per_user.md ├── 65_Shared_index.md ├── 70_Faking_it.md ├── 75_One_big_user.md └── 80_Scale_is_not_infinite.md ├── 500_Cluster_Admin ├── 10_intro.md ├── 15_marvel.md ├── 20_health.md ├── 30_node_stats.md └── 40_other_stats.md ├── 510_Deployment ├── 10_intro.md ├── 20_hardware.md ├── 30_other.md ├── 40_config.md ├── 45_dont_touch.md ├── 50_heap.md ├── 60_file_descriptors.md ├── 70_conclusion.md └── 80_cluster_settings.md ├── 520_Post_Deployment ├── 00_Intro.md ├── 10_dynamic_settings.md ├── 20_logging.md ├── 30_indexing_perf.md ├── 40_rolling_restart.md ├── 50_backup.md ├── 60_restore.md └── 70_conclusion.md ├── Changelog.md ├── README.md ├── README_Wiki.md ├── SUMMARY.md ├── TOC.md ├── config.json ├── cover.jpg ├── cover.psd ├── cover ├── background.jpg └── logo.png ├── cover_small.jpg ├── images ├── cover.png ├── elas_0201.png ├── elas_0202.png ├── elas_0203.png ├── elas_0204.png ├── elas_0205.png ├── elas_0206.png ├── elas_0301.png ├── elas_0401.png ├── elas_0402.png ├── elas_0403.png ├── elas_0404.png ├── elas_0405.png ├── elas_0406.png ├── elas_0901.png ├── elas_0902.png ├── elas_1101.png ├── elas_1102.png ├── elas_1103.png ├── elas_1104.png ├── elas_1105.png ├── elas_1106.png ├── elas_1107.png ├── elas_1108.png ├── elas_1109.png ├── elas_1110.png ├── elas_1111.png ├── elas_1701.png ├── elas_1702.png ├── elas_1703.png ├── elas_1704.png ├── elas_1705.png ├── elas_1706.png ├── elas_17in01.png ├── elas_17in02.png ├── elas_28in01.png ├── elas_28in02.png ├── elas_29in01.png ├── elas_29in02.png ├── elas_29in03.png ├── elas_33in01.png ├── elas_33in02.png ├── elas_4401.png ├── elas_4402.png ├── elas_4403.png └── elas_4404.png ├── push.sh └── update.sh /.gitignore: -------------------------------------------------------------------------------- 1 | #gitbook 2 | 3 | /_book 4 | -------------------------------------------------------------------------------- /010_Intro/00_README.md: -------------------------------------------------------------------------------- 1 | # 入门 2 | 3 | Elasticsearch是一个实时分布式搜索和分析引擎。它让你以前所未有的速度处理大数据成为可能。 4 | 5 | 它用于全文搜索、结构化搜索、分析以及将这三者混合使用: 6 | 7 | * 维基百科使用Elasticsearch提供全文搜索并高亮关键字,以及**输入实时搜索(search-as-you-type)**和**搜索纠错(did-you-mean)**等搜索建议功能。 8 | 9 | * 英国卫报使用Elasticsearch结合用户日志和社交网络数据提供给他们的编辑以实时的反馈,以便及时了解公众对新发表的文章的回应。 10 | 11 | * StackOverflow结合全文搜索与地理位置查询,以及**more-like-this**功能来找到相关的问题和答案。 12 | 13 | * Github使用Elasticsearch检索1300亿行的代码。 14 | 15 | 但是Elasticsearch不仅用于大型企业,它还让像DataDog以及Klout这样的创业公司将最初的想法变成可扩展的解决方案。Elasticsearch可以在你的笔记本上运行,也可以在数以百计的服务器上处理PB级别的数据。 16 | 17 | Elasticsearch所涉及到的每一项技术都不是创新或者革命性的,全文搜索,分析系统以及分布式数据库这些早就已经存在了。它的革命性在于将这些独立且有用的技术整合成一个一体化的、实时的应用。它对新用户的门槛很低,当然它也会跟上你技能和需求增长的步伐。 18 | 19 | 如果你打算看这本书,说明你已经有数据了,但光有数据是不够的,除非你能对这些数据做些什么事情。 20 | 21 | 很不幸,现在大部分数据库在提取可用知识方面显得异常无能。的确,它们能够通过时间戳或者精确匹配做过滤,但是它们能够进行全文搜索,处理同义词和根据相关性给文档打分吗?它们能根据同一份数据生成分析和聚合的结果吗?最重要的是,它们在没有大量工作进程(线程)的情况下能做到对数据的实时处理吗? 22 | 23 | 这就是Elasticsearch存在的理由:Elasticsearch鼓励你浏览并利用你的数据,而不是让它烂在数据库里,因为在数据库里实在太难查询了。 24 | 25 | Elasticsearch是你新认识的最好的朋友。 26 | 27 | -------------------------------------------------------------------------------- /010_Intro/05_What_is_it.md: -------------------------------------------------------------------------------- 1 | ## 为了搜索,你懂的 2 | 3 | Elasticsearch是一个基于[Apache Lucene(TM)](https://lucene.apache.org/core/)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。 4 | 5 | 但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。 6 | 7 | Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的`RESTful API`来隐藏Lucene的复杂性,从而让全文搜索变得简单。 8 | 9 | 不过,Elasticsearch不仅仅是Lucene和全文搜索,我们还能这样去描述它: 10 | 11 | * 分布式的实时文件存储,每个字段都被索引并可被搜索 12 | * 分布式的实时分析搜索引擎 13 | * 可以扩展到上百台服务器,处理PB级结构化或非结构化数据 14 | 15 | 而且,所有的这些功能被集成到一个服务里面,你的应用可以通过简单的`RESTful API`、各种语言的客户端甚至命令行与之交互。 16 | 17 | 上手Elasticsearch非常容易。它提供了许多合理的缺省值,并对初学者隐藏了复杂的搜索引擎理论。它开箱即用(安装即可使用),只需很少的学习既可在生产环境中使用。 18 | 19 | Elasticsearch在[Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0.html)下许可使用,可以免费下载、使用和修改。 20 | 21 | 随着你对Elasticsearch的理解加深,你可以根据不同的问题领域定制Elasticsearch的高级特性,这一切都是可配置的,并且配置非常灵活。 22 | 23 | -------------------------------------------------------------- 24 | **模糊的历史** 25 | 26 | 多年前,一个叫做Shay Banon的刚结婚不久的失业开发者,由于妻子要去伦敦学习厨师,他便跟着也去了。在他找工作的过程中,为了给妻子构建一个食谱的搜索引擎,他开始构建一个早期版本的Lucene。 27 | 28 | 直接基于Lucene工作会比较困难,所以Shay开始抽象Lucene代码以便Java程序员可以在应用中添加搜索功能。他发布了他的第一个开源项目,叫做“Compass”。 29 | 30 | 后来Shay找到一份工作,这份工作处在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引擎也是理所当然需要的。然后他决定重写Compass库使其成为一个独立的服务叫做Elasticsearch。 31 | 32 | 第一个公开版本出现在2010年2月,在那之后Elasticsearch已经成为Github上最受欢迎的项目之一,代码贡献者超过300人。一家主营Elasticsearch的公司就此成立,他们一边提供商业支持一边开发新功能,不过Elasticsearch将永远开源且对所有人可用。 33 | 34 | Shay的妻子依旧等待着她的食谱搜索…… 35 | -------------------------------------------------------------------------------- /010_Intro/20_Document.md: -------------------------------------------------------------------------------- 1 | ## 面向文档 2 | 3 | 应用中的对象很少只是简单的键值列表,更多时候它拥有复杂的数据结构,比如包含日期、地理位置、另一个对象或者数组。 4 | 5 | 总有一天你会想到把这些对象存储到数据库中。将这些数据保存到由行和列组成的关系数据库中,就好像是把一个丰富,信息表现力强的对象拆散了放入一个非常大的表格中:你不得不拆散对象以适应表模式(通常一列表示一个字段),然后又不得不在查询的时候重建它们。 6 | 7 | Elasticsearch是**面向文档(document oriented)**的,这意味着它可以存储整个对象或**文档(document)**。然而它不仅仅是存储,还会**索引(index)**每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一。 8 | 9 | ## JSON 10 | ELasticsearch使用**Javascript对象符号(JavaScript 11 | Object Notation)**,也就是[**JSON**](http://en.wikipedia.org/wiki/Json),作为文档序列化格式。JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。它简洁、简单且容易阅读。 12 | 13 | 以下使用JSON文档来表示一个用户对象: 14 | 15 | ```Javascript 16 | { 17 | "email": "john@smith.com", 18 | "first_name": "John", 19 | "last_name": "Smith", 20 | "info": { 21 | "bio": "Eco-warrior and defender of the weak", 22 | "age": 25, 23 | "interests": [ "dolphins", "whales" ] 24 | }, 25 | "join_date": "2014/05/01" 26 | } 27 | ``` 28 | 29 | 尽管原始的`user`对象很复杂,但它的结构和对象的含义已经被完整的体现在JSON中了,在Elasticsearch中将对象转化为JSON并做索引要比在表结构中做相同的事情简单的多。 30 | 31 | >**NOTE** 32 | 33 | >尽管几乎所有的语言都有相应的模块用于将任意数据结构转换为JSON,但每种语言处理细节不同。具体请查看“`serialization`” or “`marshalling`”两个用于处理JSON的模块。[Elasticsearch官方客户端](http://www.elasticsearch.org/guide)会自动为你序列化和反序列化JSON。 34 | -------------------------------------------------------------------------------- /010_Intro/40_Tutorial_Conclusion.md: -------------------------------------------------------------------------------- 1 | ## 教程小结 2 | 3 | 希望这个简短的教程能够很好的描述Elasticsearch的功能。当然这只是一些皮毛,为了保持简短,还有很多的特性未提及——像推荐、定位、渗透、模糊以及部分匹配等。但这也突出了构建高级搜索功能是多么的容易。无需配置,只需要添加数据然后开始搜索! 4 | 5 | 可能有些语法让你觉得有些困惑,或者在微调方面有些疑问。那么,本书的其余部分将深入这些问题的细节,让你全面了解Elasticsearch的工作过程。 6 | -------------------------------------------------------------------------------- /010_Intro/45_Distributed.md: -------------------------------------------------------------------------------- 1 | ## 分布式的特性 2 | 3 | 在章节的开始我们提到Elasticsearch可以扩展到上百(甚至上千)的服务器来处理PB级的数据。然而我们的教程只是给出了一些使用Elasticsearch的例子,并未涉及相关机制。Elasticsearch为分布式而生,而且它的设计隐藏了分布式本身的复杂性。 4 | 5 | Elasticsearch在分布式概念上做了很大程度上的透明化,在教程中你不需要知道任何关于分布式系统、分片、集群发现或者其他大量的分布式概念。所有的教程你既可以运行在你的笔记本上,也可以运行在拥有100个节点的集群上,其工作方式是一样的。 6 | 7 | Elasticsearch致力于隐藏分布式系统的复杂性。以下这些操作都是在底层自动完成的: 8 | 9 | * 将你的文档分区到不同的容器或者**分片(shards)**中,它们可以存在于一个或多个节点中。 10 | * 将分片均匀的分配到各个节点,对索引和搜索做负载均衡。 11 | * 冗余每一个分片,防止硬件故障造成的数据丢失。 12 | * 将集群中任意一个节点上的请求路由到相应数据所在的节点。 13 | * 无论是增加节点,还是移除节点,分片都可以做到无缝的扩展和迁移。 14 | 15 | 当你阅读本书时,你可以遇到关于Elasticsearch分布式特性的补充章节。这些章节将教给你如何扩展集群和故障转移,如何处理文档存储,如何执行分布式搜索,分片是什么以及如何工作。 16 | 17 | 这些章节不是必读的——不懂这些内部机制也可以使用Elasticsearch的。但是这些能够帮助你更深入和完整的了解Elasticsearch。你可以略读它们,然后在你需要更深入的理解时再回头翻阅。 18 | -------------------------------------------------------------------------------- /010_Intro/50_Conclusion.md: -------------------------------------------------------------------------------- 1 | ## 下一步 2 | 现在你对Elasticsearch可以做些什么以及其易用程度有了大概的了解。Elasticsearch致力于降低学习成本和轻松配置。学习Elasticsearch最好的方式就是开始使用它:开始索引和检索吧! 3 | 4 | 当然,你越是了解Elasticsearch,你的生产力就越高。你越是详细告诉Elasticsearch你的应用的数据特点,你就越能得到准确的输出。 5 | 6 | 本书其余部分将帮助你从新手晋级到专家。每一个章节都会阐述一个要点,并且会包含专家级别的技巧。如果你只是刚起步,那么这些技巧可能暂时和你无关。Elasticsearch有合理的默认配置而且可以在没有用户干预的情况下做正确的事情。当需要提升性能时你可以随时回顾这些章节。 7 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 集群内部工作方式 2 | 3 | >### 补充章节 4 | 5 | >正如之前提及的,这是关于Elasticsearch在分布式环境下工作机制的一些补充章节的第一部分。这个章节我们解释一些通用的术语,例如**集群(cluster)**、**节点(node)**和**分片(shard)**,Elasticsearch的扩展机制,以及它如何处理硬件故障。 6 | 7 | >尽管这章不是必读的——你在使用Elasticsearch的时候可以长时间甚至永远都不必担心分片、复制和故障转移——但是它会帮助你理解Elasticsearch内部的工作流程,你可以先跳过这章,以后再来查阅。 8 | 9 | Elasticsearch用于构建高可用和可扩展的系统。扩展的方式可以是购买更好的服务器(**纵向扩展(vertical scale or scaling up)**)或者购买更多的服务器(**横向扩展(horizontal scale or scaling out)**)。 10 | 11 | Elasticsearch虽然能从更强大的硬件中获得更好的性能,但是纵向扩展有它的局限性。真正的扩展应该是横向的,它通过增加节点来均摊负载和增加可靠性。 12 | 13 | 对于大多数数据库而言,横向扩展意味着你的程序将做非常大的改动才能利用这些新添加的设备。对比来说,Elasticsearch天生就是分布式的:它知道如何管理节点来提供高扩展和高可用。这意味着你的程序不需要关心这些。 14 | 15 | 在这章我们将探索如何创建你的**集群(cluster)**、**节点(node)**和**分片(shards)**,使其按照你的需求进行扩展,并保证在硬件故障时数据依旧安全。 16 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/05_Empty_cluster.md: -------------------------------------------------------------------------------- 1 | ## 空集群 2 | 3 | 如果我们启动一个单独的节点,它还没有数据和索引,这个集群看起来就像图1。 4 | 5 | 6 | 7 | ![A cluster with one empty node](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0201.png) 8 | 图1:只有一个空节点的集群 9 | 10 | 11 | 一个**节点(node)**就是一个Elasticsearch实例,而一个**集群(cluster)**由一个或多个节点组成,它们具有相同的`cluster.name`,它们协同工作,分享数据和负载。当加入新的节点或者删除一个节点时,集群就会感知到并平衡数据。 12 | 13 | 集群中一个节点会被选举为**主节点(master)**,它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,这意味着在流量增长的时候,该主节点不会成为集群的瓶颈。任何节点都可以成为主节点。我们例子中的集群只有一个节点,所以它会充当主节点的角色。 14 | 15 | 做为用户,我们能够与集群中的任何节点通信,包括主节点。每一个节点都知道文档存在于哪个节点上,它们可以转发请求到相应的节点上。我们访问的节点负责收集各节点返回的数据,最后一起返回给客户端。这一切都由Elasticsearch处理。 -------------------------------------------------------------------------------- /020_Distributed_Cluster/10_Cluster_health.md: -------------------------------------------------------------------------------- 1 | ## 集群健康 2 | 3 | 在Elasticsearch集群中可以监控统计很多信息,但是只有一个是最重要的:**集群健康(cluster health)**。集群健康有三种状态:`green`、`yellow`或`red`。 4 | 5 | ```Javascript 6 | GET /_cluster/health 7 | ``` 8 | 在一个没有索引的空集群中运行如上查询,将返回这些信息: 9 | 10 | ```Javascript 11 | { 12 | "cluster_name": "elasticsearch", 13 | "status": "green", <1> 14 | "timed_out": false, 15 | "number_of_nodes": 1, 16 | "number_of_data_nodes": 1, 17 | "active_primary_shards": 0, 18 | "active_shards": 0, 19 | "relocating_shards": 0, 20 | "initializing_shards": 0, 21 | "unassigned_shards": 0 22 | } 23 | ``` 24 | - <1> `status` 是我们最感兴趣的字段 25 | 26 | `status`字段提供一个综合的指标来表示集群的的服务状况。三种颜色各自的含义: 27 | 28 | | 颜色 | 意义 | 29 | | -------- | ---------------------------------------- | 30 | | `green` | 所有主要分片和复制分片都可用 | 31 | | `yellow` | 所有主要分片可用,但不是所有复制分片都可用 | 32 | | `red` | 不是所有的主要分片都可用 | 33 | 34 | 在接下来的章节,我们将说明什么是**主要分片(primary shard)**和**复制分片(replica shard)**,并说明这些颜色(状态)在实际环境中的意义。 35 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/15_Add_an_index.md: -------------------------------------------------------------------------------- 1 | ## 添加索引 2 | 3 | 为了将数据添加到Elasticsearch,我们需要**索引(index)**——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个**分片(shards)**的**“逻辑命名空间(logical namespace)”**. 4 | 5 | 一个**分片(shard)**是一个最小级别**“工作单元(worker unit)”**,它只是保存了索引中所有数据的一部分。在接下来的《深入分片》一章,我们将详细说明分片的工作原理,但是现在我们只要知道分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。我们的文档存储在分片中,并且在分片中被索引,但是我们的应用程序不会直接与它们通信,取而代之的是,直接与索引通信。 6 | 7 | 分片是Elasticsearch在集群中分发数据的关键。把分片想象成数据的容器。文档存储在分片中,然后分片分配到你集群中的节点上。当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。 8 | 9 | 分片可以是**主分片(primary shard)**或者是**复制分片(replica shard)**。你索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。 10 | 11 | > 理论上主分片能存储的数据大小是没有限制的,限制取决于你实际的使用情况。分片的最大容量完全取决于你的使用状况:硬件存储的大小、文档的大小和复杂度、如何索引和查询你的文档,以及你期望的响应时间。 12 | 13 | 复制分片只是主分片的一个副本,它可以防止硬件故障导致的数据丢失,同时可以提供读请求,比如搜索或者从别的shard取回文档。 14 | 15 | 当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整。 16 | 17 | 让我们在集群中唯一一个空节点上创建一个叫做`blogs`的索引。默认情况下,一个索引被分配5个主分片,但是为了演示的目的,我们只分配3个主分片和一个复制分片(每个主分片都有一个复制分片): 18 | 19 | ```Javascript 20 | PUT /blogs 21 | { 22 | "settings" : { 23 | "number_of_shards" : 3, 24 | "number_of_replicas" : 1 25 | } 26 | } 27 | ``` 28 | 29 | 附带索引的单一节点集群: 30 | ![有一个索引的单一节点集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0202.png) 31 | 32 | 我们的集群现在看起来就像上图——三个主分片都被分配到`Node 1`。如果我们现在检查**集群健康(cluster-health)**,我们将见到以下信息: 33 | 34 | ```Javascript 35 | { 36 | "cluster_name": "elasticsearch", 37 | "status": "yellow", <1> 38 | "timed_out": false, 39 | "number_of_nodes": 1, 40 | "number_of_data_nodes": 1, 41 | "active_primary_shards": 3, 42 | "active_shards": 3, 43 | "relocating_shards": 0, 44 | "initializing_shards": 0, 45 | "unassigned_shards": 3 <2> 46 | } 47 | ``` 48 | 49 | - <1> 集群的状态现在是 `yellow` 50 | - <2> 我们的三个复制分片还没有被分配到节点上 51 | 52 | 集群的健康状态`yellow`表示所有的**主分片(primary shards)**启动并且正常运行了——集群已经可以正常处理任何请求——但是**复制分片(replica shards)**还没有全部可用。事实上所有的三个复制分片现在都是`unassigned`状态——它们还未被分配给节点。在同一个节点上保存相同的数据副本是没有必要的,如果这个节点故障了,那所有的数据副本也会丢失。 53 | 54 | 现在我们的集群已经功能完备,但是依旧存在因硬件故障而导致数据丢失的风险。 55 | 56 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/20_Add_failover.md: -------------------------------------------------------------------------------- 1 | ## 增加故障转移 2 | 3 | 在单一节点上运行意味着有单点故障的风险——没有数据备份。幸运的是,要防止单点故障,我们唯一需要做的就是启动另一个节点。 4 | 5 | > ### 启动第二个节点 6 | 7 | > 为了测试在增加第二个节点后发生了什么,你可以使用与第一个节点相同的方式启动第二个节点(《运行Elasticsearch》一章),而且命令行在同一个目录——一个节点可以启动多个Elasticsearch实例。 8 | 9 | > 只要第二个节点与第一个节点有相同的`cluster.name`(请看`./config/elasticsearch.yml`文件),它就能自动发现并加入第一个节点所在的集群。如果没有,检查日志找出哪里出了问题。这可能是网络广播被禁用,或者防火墙阻止了节点通信。 10 | 11 | 如果我们启动了第二个节点,这个集群看起来就像下图。 12 | 13 | 双节点集群——所有的主分片和复制分片都已分配: 14 | ![双节点集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0203.png) 15 | 16 | 第二个节点已经加入集群,三个**复制分片(replica shards)**也已经被分配了——分别对应三个主分片,这意味着在丢失任意一个节点的情况下依旧可以保证数据的完整性。 17 | 18 | 文档的索引将首先被存储在主分片中,然后并发复制到对应的复制节点上。这可以确保我们的数据在主节点和复制节点上都可以被检索。 19 | 20 | `cluster-health`现在的状态是`green`,这意味着所有的6个分片(三个主分片和三个复制分片)都已可用: 21 | 22 | ```Javascript 23 | { 24 | "cluster_name": "elasticsearch", 25 | "status": "green", <1> 26 | "timed_out": false, 27 | "number_of_nodes": 2, 28 | "number_of_data_nodes": 2, 29 | "active_primary_shards": 3, 30 | "active_shards": 6, 31 | "relocating_shards": 0, 32 | "initializing_shards": 0, 33 | "unassigned_shards": 0 34 | } 35 | ``` 36 | 37 | - <1> 集群的状态是`green`. 38 | 39 | 我们的集群不仅是功能完备的,而且是高可用的。 40 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/25_Scale_horizontally.md: -------------------------------------------------------------------------------- 1 | ## 横向扩展 2 | 3 | 随着应用需求的增长,我们该如何扩展?如果我们启动第三个节点,我们的集群会重新组织自己,就像图4: 4 | 5 | 6 | 图4:包含3个节点的集群——分片已经被重新分配以平衡负载: 7 | ![三节点集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0204.png) 8 | 9 | `Node3`包含了分别来自`Node 1`和`Node 2`的一个分片,这样每个节点就有两个分片,和之前相比少了一个,这意味着每个节点上的分片将获得更多的硬件资源(CPU、RAM、I/O)。 10 | 11 | 分片本身就是一个完整的搜索引擎,它可以使用单一节点的所有资源。我们拥有6个分片(3个主分片和三个复制分片),最多可以扩展到6个节点,每个节点上有一个分片,每个分片可以100%使用这个节点的资源。 12 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/30_Scale_more.md: -------------------------------------------------------------------------------- 1 | ## 继续扩展 2 | 如果我们要扩展到6个以上的节点,要怎么做? 3 | 4 | 主分片的数量在创建索引时已经确定。实际上,这个数量定义了能存储到索引里数据的最大数量(实际的数量取决于你的数据、硬件和应用场景)。然而,主分片或者复制分片都可以处理读请求——搜索或文档检索,所以数据的冗余越多,我们能处理的搜索吞吐量就越大。 5 | 6 | 复制分片的数量可以在运行中的集群中动态地变更,这允许我们可以根据需求扩大或者缩小规模。让我们把复制分片的数量从原来的`1`增加到`2`: 7 | 8 | ```Javascript 9 | PUT /blogs/_settings 10 | { 11 | "number_of_replicas" : 2 12 | } 13 | ``` 14 | 15 | 图5:增加`number_of_replicas`到2: 16 | ![三节点两复制集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0205.png) 17 | 18 | 从图中可以看出,`blogs`索引现在有9个分片:3个主分片和6个复制分片。这意味着我们能够扩展到9个节点,再次变成每个节点一个分片。这样使我们的搜索性能相比原始的三节点集群增加**三倍**。 19 | 20 | > 当然,在同样数量的节点上增加更多的复制分片并不能提高性能,因为这样做的话平均每个分片的所占有的硬件资源就减少了(译者注:大部分请求都聚集到了分片少的节点,导致一个节点吞吐量太大,反而降低性能),你需要增加硬件来提高吞吐量。 21 | 22 | > 不过这些额外的复制节点使我们有更多的冗余:通过以上对节点的设置,我们能够承受两个节点故障而不丢失数据。 23 | -------------------------------------------------------------------------------- /020_Distributed_Cluster/35_Coping_with_failure.md: -------------------------------------------------------------------------------- 1 | ## 应对故障 2 | 3 | 我们已经说过Elasticsearch可以应对节点失效,所以让我们继续尝试。如果我们杀掉第一个节点的进程(以下简称杀掉节点),我们的集群看起来就像这样: 4 | 5 | 图5:杀掉第一个节点后的集群 6 | 7 | ![杀掉一个节点后的集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0206.png) 8 | 9 | 我们杀掉的节点是一个主节点。一个集群必须要有一个主节点才能使其功能正常,所以集群做的第一件事就是各节点选举了一个新的主节点:`Node 2`。 10 | 11 | 主分片`1`和`2`在我们杀掉`Node 1`时已经丢失,我们的索引在丢失主分片时不能正常工作。如果此时我们检查集群健康,我们将看到状态`red`:不是所有主分片都可用! 12 | 13 | 幸运的是丢失的两个主分片的完整拷贝存在于其他节点上,所以新主节点做的第一件事是把这些在`Node 2`和`Node 3`上的复制分片升级为主分片,这时集群健康回到`yellow`状态。这个提升是瞬间完成的,就好像按了一下开关。 14 | 15 | 为什么集群健康状态是`yellow`而不是`green`?我们有三个主分片,但是我们指定了每个主分片对应两个复制分片,当前却只有一个复制分片被分配,这就是集群状态无法达到`green`的原因,不过不用太担心这个:当我们杀掉`Node 2`,我们的程序依然可以在没有丢失数据的情况下继续运行,因为`Node 3`还有每个分片的拷贝。 16 | 17 | 如果我们重启`Node 1`,集群将能够重新分配丢失的复制分片,集群状况与上一节的 **图5:增加number_of_replicas到2** 类似。如果`Node 1`依旧有旧分片的拷贝,它将会尝试再利用它们,它只会从主分片上复制在故障期间有数据变更的那一部分。 18 | 19 | 现在你应该对分片如何使Elasticsearch可以水平扩展并保证数据安全有了一个清晰的认识。接下来我们将会讨论分片生命周期的更多细节。 20 | -------------------------------------------------------------------------------- /030_Data/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 数据吞吐 2 | 3 | 无论程序怎么写,意图是一样的:组织数据为我们的目标所服务。但数据并不只是由随机比特和字节组成,我们在数据节点间建立关联来表示现实世界中的实体或者“某些东西”。属于同一个人的名字和Email地址会有更多的意义。 4 | 5 | 在现实世界中,并不是所有相同类型的实体看起来都是一样的。一个人可能有一个家庭电话号码,另一个人可能只有一个手机号码,有些人可能两者都有。一个人可能有三个Email地址,其他人可能没有。西班牙人可能有两个姓氏,但是英国人(英语系国家的人)可能只有一个。 6 | 7 | 面向对象编程语言流行的原因之一,是我们可以用对象来表示和处理现实生活中那些有着潜在关系和复杂结构的实体。到目前为止,这种方式还不错。 8 | 9 | 但当我们想存储这些实体时问题便来了。传统上,我们以行和列的形式把数据存储在关系型数据库中,相当于使用电子表格。这种固定的存储方式导致对象的灵活性不复存在了。 10 | 11 | 但是如何能以对象的形式存储对象呢?相对于围绕表格去为我们的程序去建模,我们可以专注于**使用**数据,把对象本来的灵活性找回来。 12 | 13 | **对象(object)**是一种语言相关,记录在内存中的的数据结构。为了在网络间发送,或者存储它,我们需要一些标准的格式来表示它。[JSON (JavaScript Object Notation)](http://en.wikipedia.org/wiki/Json)是一种可读的以文本来表示对象的方式。它已经成为NoSQL世界中数据交换的一种事实标准。当对象被序列化为JSON,它就成为**JSON文档(JSON document)**了。 14 | 15 | Elasticsearch是一个分布式的**文档(document)**存储引擎。它可以实时存储并检索复杂数据结构——序列化的JSON文档。换言说,一旦文档被存储在Elasticsearch中,它就可以在集群的任一节点上被检索。 16 | 17 | 当然,我们不仅需要存储数据,还要快速的**批量**查询。虽然已经有很多NoSQL的解决方案允许我们以文档的形式存储对象,但它们依旧需要考虑如何查询这些数据,以及哪些字段需要被索引以便检索时更加快速。 18 | 19 | 在Elasticsearch中,**每一个字段的数据**都是**默认被索引**的。也就是说,每个字段专门有一个反向索引用于快速检索。而且,与其它数据库不同,它可以在**同一个查询中**利用所有的这些反向索引,以惊人的速度返回结果。 20 | 21 | 在这一章我们将探讨如何使用API来创建、检索、更新和删除文档。目前,我们并不关心数据如何在文档中以及如何查询他们。所有我们关心的是文档如何安全在Elasticsearch中存储,以及如何让它们返回。 22 | -------------------------------------------------------------------------------- /030_Data/05_Document.md: -------------------------------------------------------------------------------- 1 | ## 什么是文档? 2 | 3 | 程序中大多的实体或对象能够被序列化为包含键值对的JSON对象,**键(key)**是**字段(field)**或**属性(property)**的名字,**值(value)**可以是字符串、数字、布尔类型、另一个对象、值数组或者其他特殊类型,比如表示日期的字符串或者表示地理位置的对象。 4 | 5 | ```Javascript 6 | { 7 | "name": "John Smith", 8 | "age": 42, 9 | "confirmed": true, 10 | "join_date": "2014-06-01", 11 | "home": { 12 | "lat": 51.5, 13 | "lon": 0.1 14 | }, 15 | "accounts": [ 16 | { 17 | "type": "facebook", 18 | "id": "johnsmith" 19 | }, 20 | { 21 | "type": "twitter", 22 | "id": "johnsmith" 23 | } 24 | ] 25 | } 26 | ``` 27 | 28 | 通常,我们可以认为**对象(object)**和**文档(document)**是等价相通的。不过,他们还是有所差别:对象(Object)是一个JSON结构体——类似于哈希、hashmap、字典或者关联数组;对象(Object)中还可能包含其他对象(Object)。 29 | 在Elasticsearch中,**文档(document)**这个术语有着特殊含义。它特指最顶层结构或者**根对象(root object)**序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中)。 30 | 31 | ## 文档元数据 32 | 33 | 一个文档不只有数据。它还包含了**元数据(metadata)**——**关于**文档的信息。三个必须的元数据节点是: 34 | 35 | | 节点 | 说明 | 36 | | -------- | ------------------ | 37 | | `_index` | 文档存储的地方 | 38 | | `_type` | 文档代表的对象的类 | 39 | | `_id` | 文档的唯一标识 | 40 | 41 | 42 | ### `_index` 43 | 44 | **索引(index)**类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。 45 | 46 | > 提示: 47 | 48 | > 事实上,我们的数据被存储和索引在**分片(shards)**中,索引只是一个把一个或多个分片分组在一起的逻辑空间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在**索引(index)**中。剩下的细节由Elasticsearch关心既可。 49 | 50 | 我们将会在《索引管理》章节中探讨如何创建并管理索引,但现在,我们将让Elasticsearch为我们创建索引。我们唯一需要做的仅仅是选择一个索引名。这个名字必须是全部小写,不能以下划线开头,不能包含逗号。让我们使用`website`做为索引名。 51 | 52 | ### `_type` 53 | 54 | 在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一个**类(class)**,这个类定义了属性或与对象关联的数据。`user`类的对象可能包含姓名、性别、年龄和Email地址。 55 | 56 | 在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为它们有着相同的结构。同理,在Elasticsearch中,我们使用相同**类型(type)**的文档表示相同的“事物”,因为他们的数据结构也是相同的。 57 | 58 | 每个**类型(type)**都有自己的**映射(mapping)**或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储在同一个索引下,但是类型的**映射(mapping)**会告诉Elasticsearch不同的文档如何被索引。 59 | 我们将会在《映射》章节探讨如何定义和管理映射,但是现在我们将依赖Elasticsearch去自动处理数据结构。 60 | 61 | `_type`的名字可以是大写或小写,不能包含下划线或逗号。我们将使用`blog`做为类型名。 62 | 63 | ### `_id` 64 | 65 | **id**仅仅是一个字符串,它与`_index`和`_type`组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义`_id`,也可以让Elasticsearch帮你自动生成。 66 | 67 | ### 其它元数据 68 | 69 | 还有一些其它的元数据,我们将在《映射》章节探讨。使用上面提到的元素,我们已经可以在Elasticsearch中存储文档并通过ID检索——换言说,把Elasticsearch做为文档存储器使用了。 70 | -------------------------------------------------------------------------------- /030_Data/10_Index.md: -------------------------------------------------------------------------------- 1 | ## 索引一个文档 2 | 3 | 文档通过`index` API被索引——使数据可以被存储和搜索。但是首先我们需要决定文档所在。正如我们讨论的,文档通过其`_index`、`_type`、`_id`唯一确定。们可以自己提供一个`_id`,或者也使用`index` API 为我们生成一个。 4 | 5 | ### 使用自己的ID 6 | 7 | 如果你的文档有自然的标识符(例如`user_account`字段或者其他值表示文档),你就可以提供自己的`_id`,使用这种形式的`index` API: 8 | 9 | ```Javascript 10 | PUT /{index}/{type}/{id} 11 | { 12 | "field": "value", 13 | ... 14 | } 15 | ``` 16 | 17 | 例如我们的索引叫做`“website”`,类型叫做`“blog”`,我们选择的ID是`“123”`,那么这个索引请求就像这样: 18 | 19 | ```Javascript 20 | PUT /website/blog/123 21 | { 22 | "title": "My first blog entry", 23 | "text": "Just trying this out...", 24 | "date": "2014/01/01" 25 | } 26 | ``` 27 | 28 | Elasticsearch的响应: 29 | 30 | ```Javascript 31 | { 32 | "_index": "website", 33 | "_type": "blog", 34 | "_id": "123", 35 | "_version": 1, 36 | "created": true 37 | } 38 | ``` 39 | 40 | 响应指出请求的索引已经被成功创建,这个索引中包含`_index`、`_type`和`_id`元数据,以及一个新元素:`_version`。 41 | 42 | Elasticsearch中每个文档都有版本号,每当文档变化(包括删除)都会使`_version`增加。在《版本控制》章节中我们将探讨如何使用`_version`号确保你程序的一部分不会覆盖掉另一部分所做的更改。 43 | 44 | ### 自增ID 45 | 46 | 如果我们的数据没有自然ID,我们可以让Elasticsearch自动为我们生成。请求结构发生了变化:`PUT`方法——`“在这个URL中存储文档”`变成了`POST`方法——`"在这个类型下存储文档"`。(译者注:原来是把文档存储到某个ID对应的空间,现在是把这个文档添加到某个`_type`下)。 47 | 48 | URL现在只包含`_index`和`_type`两个字段: 49 | 50 | ```Javascript 51 | POST /website/blog/ 52 | { 53 | "title": "My second blog entry", 54 | "text": "Still trying this out...", 55 | "date": "2014/01/01" 56 | } 57 | ``` 58 | 59 | 响应内容与刚才类似,只有`_id`字段变成了自动生成的值: 60 | 61 | ```Javascript 62 | { 63 | "_index": "website", 64 | "_type": "blog", 65 | "_id": "wM0OSFhDQXGZAWDf0-drSA", 66 | "_version": 1, 67 | "created": true 68 | } 69 | ``` 70 | 71 | 自动生成的ID有22个字符长,URL-safe, Base64-encoded string universally unique identifiers, 或者叫 [UUIDs](http://en.wikipedia.org/wiki/Uuid)。 72 | 73 | -------------------------------------------------------------------------------- /030_Data/15_Get.md: -------------------------------------------------------------------------------- 1 | ## 检索文档 2 | 3 | 想要从Elasticsearch中获取文档,我们使用同样的`_index`、`_type`、`_id`,但是HTTP方法改为`GET`: 4 | 5 | ```Javascript 6 | GET /website/blog/123?pretty 7 | ``` 8 | 响应包含了现在熟悉的元数据节点,增加了`_source`字段,它包含了在创建索引时我们发送给Elasticsearch的原始文档。 9 | 10 | ```Javascript 11 | { 12 | "_index" : "website", 13 | "_type" : "blog", 14 | "_id" : "123", 15 | "_version" : 1, 16 | "found" : true, 17 | "_source" : { 18 | "title": "My first blog entry", 19 | "text": "Just trying this out...", 20 | "date": "2014/01/01" 21 | } 22 | } 23 | ``` 24 | 25 | > ### `pretty` 26 | 27 | >在任意的查询字符串中增加`pretty`参数,类似于上面的例子。会让Elasticsearch**美化输出(pretty-print)**JSON响应以便更加容易阅读。`_source`字段不会被美化,它的样子与我们输入的一致。 28 | 29 | GET请求返回的响应内容包括`{"found": true}`。这意味着文档已经找到。如果我们请求一个不存在的文档,依旧会得到一个JSON,不过`found`值变成了`false`。 30 | 31 | 此外,HTTP响应状态码也会变成`'404 Not Found'`代替`'200 OK'`。我们可以在`curl`后加`-i`参数得到响应头: 32 | 33 | ```sh 34 | curl -i -XGET http://localhost:9200/website/blog/124?pretty 35 | ``` 36 | 37 | 现在响应类似于这样: 38 | 39 | ```Javascript 40 | HTTP/1.1 404 Not Found 41 | Content-Type: application/json; charset=UTF-8 42 | Content-Length: 83 43 | 44 | { 45 | "_index" : "website", 46 | "_type" : "blog", 47 | "_id" : "124", 48 | "found" : false 49 | } 50 | ``` 51 | 52 | ### 检索文档的一部分 53 | 54 | 通常,`GET`请求将返回文档的全部,存储在`_source`参数中。但是可能你感兴趣的字段只是`title`。请求个别字段可以使用`_source`参数。多个字段可以使用逗号分隔: 55 | 56 | ```sh 57 | GET /website/blog/123?_source=title,text 58 | ``` 59 | 60 | `_source`字段现在只包含我们请求的字段,而且过滤了`date`字段: 61 | 62 | ```Javascript 63 | { 64 | "_index" : "website", 65 | "_type" : "blog", 66 | "_id" : "123", 67 | "_version" : 1, 68 | "exists" : true, 69 | "_source" : { 70 | "title": "My first blog entry" , 71 | "text": "Just trying this out..." 72 | } 73 | } 74 | ``` 75 | 76 | 或者你只想得到`_source`字段而不要其他的元数据,你可以这样请求: 77 | 78 | ```sh 79 | GET /website/blog/123/_source 80 | ``` 81 | 它仅仅返回: 82 | 83 | ```Javascript 84 | { 85 | "title": "My first blog entry", 86 | "text": "Just trying this out...", 87 | "date": "2014/01/01" 88 | } 89 | ``` 90 | -------------------------------------------------------------------------------- /030_Data/20_Exists.md: -------------------------------------------------------------------------------- 1 | ## 检查文档是否存在 2 | 3 | 如果你想做的只是检查文档是否存在——你对内容完全不感兴趣——使用`HEAD`方法来代替`GET`。`HEAD`请求不会返回响应体,只有HTTP头: 4 | 5 | ```Javascript 6 | curl -i -XHEAD http://localhost:9200/website/blog/123 7 | ``` 8 | 9 | Elasticsearch将会返回`200 OK`状态如果你的文档存在: 10 | 11 | ```Javascript 12 | HTTP/1.1 200 OK 13 | Content-Type: text/plain; charset=UTF-8 14 | Content-Length: 0 15 | ``` 16 | 17 | 如果不存在返回`404 Not Found`: 18 | 19 | ```Javascript 20 | curl -i -XHEAD http://localhost:9200/website/blog/124 21 | ``` 22 | 23 | ```Javascript 24 | HTTP/1.1 404 Not Found 25 | Content-Type: text/plain; charset=UTF-8 26 | Content-Length: 0 27 | ``` 28 | 29 | 当然,这只表示你在查询的那一刻文档不存在,但并不表示几毫秒后依旧不存在。另一个进程在这期间可能创建新文档。 30 | -------------------------------------------------------------------------------- /030_Data/25_Update.md: -------------------------------------------------------------------------------- 1 | ## 更新整个文档 2 | 3 | 文档在Elasticsearch中是不可变的——我们不能修改他们。如果需要更新已存在的文档,我们可以使用《索引文档》章节提到的`index` API _重建索引(reindex)_ 或者替换掉它。 4 | 5 | ```Javascript 6 | PUT /website/blog/123 7 | { 8 | "title": "My first blog entry", 9 | "text": "I am starting to get the hang of this...", 10 | "date": "2014/01/02" 11 | } 12 | ``` 13 | 14 | 在响应中,我们可以看到Elasticsearch把`_version`增加了。 15 | 16 | ```Javascript 17 | { 18 | "_index" : "website", 19 | "_type" : "blog", 20 | "_id" : "123", 21 | "_version" : 2, 22 | "created": false <1> 23 | } 24 | ``` 25 | 26 | - <1> `created`标识为`false`因为同索引、同类型下已经存在同ID的文档。 27 | 28 | 在内部,Elasticsearch已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。Elasticsearch会在你继续索引更多数据时清理被删除的文档。 29 | 30 | 在本章的后面,我们将会在《局部更新》中探讨`update` API。这个API *似乎* 允许你修改文档的局部,但事实上Elasticsearch遵循与之前所说完全相同的过程,这个过程如下: 31 | 32 | 1. 从旧文档中检索JSON 33 | 2. 修改它 34 | 3. 删除旧文档 35 | 4. 索引新文档 36 | 37 | 唯一的不同是`update` API完成这一过程只需要一个客户端请求既可,不再需要`get`和`index`请求了。 38 | 39 | -------------------------------------------------------------------------------- /030_Data/30_Create.md: -------------------------------------------------------------------------------- 1 | ## 创建一个新文档 2 | 3 | 当索引一个文档,我们如何确定是完全创建了一个新的还是覆盖了一个已经存在的呢? 4 | 5 | 请记住`_index`、`_type`、`_id`三者唯一确定一个文档。所以要想保证文档是新加入的,最简单的方式是使用`POST`方法让Elasticsearch自动生成唯一`_id`: 6 | 7 | ```Javascript 8 | POST /website/blog/ 9 | { ... } 10 | ``` 11 | 12 | 然而,如果想使用自定义的`_id`,我们必须告诉Elasticsearch应该在`_index`、`_type`、`_id`三者都不同时才接受请求。为了做到这点有两种方法,它们其实做的是同一件事情。你可以选择适合自己的方式: 13 | 14 | 第一种方法使用`op_type`查询参数: 15 | 16 | ```Javascript 17 | PUT /website/blog/123?op_type=create 18 | { ... } 19 | ``` 20 | 21 | 或者第二种方法是在URL后加`/_create`做为端点: 22 | 23 | ```Javascript 24 | PUT /website/blog/123/_create 25 | { ... } 26 | ``` 27 | 28 | 如果请求成功的创建了一个新文档,Elasticsearch将返回正常的元数据且响应状态码是`201 Created`。 29 | 30 | 另一方面,如果包含相同的`_index`、`_type`和`_id`的文档已经存在,Elasticsearch将返回`409 Conflict`响应状态码,错误信息类似如下: 31 | 32 | ```Javascript 33 | { 34 | "error" : "DocumentAlreadyExistsException[[website][4] [blog][123]: 35 | document already exists]", 36 | "status" : 409 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /030_Data/35_Delete.md: -------------------------------------------------------------------------------- 1 | ## 删除文档 2 | 3 | 删除文档的语法模式与之前基本一致,只不过要使用`DELETE`方法: 4 | 5 | ```Javascript 6 | DELETE /website/blog/123 7 | ``` 8 | 9 | 如果文档被找到,Elasticsearch将返回`200 OK`状态码和以下响应体。注意`_version`数字已经增加了。 10 | 11 | ```Javascript 12 | { 13 | "found" : true, 14 | "_index" : "website", 15 | "_type" : "blog", 16 | "_id" : "123", 17 | "_version" : 3 18 | } 19 | ``` 20 | 21 | 如果文档未找到,我们将得到一个`404 Not Found`状态码,响应体是这样的: 22 | 23 | ```Javascript 24 | { 25 | "found" : false, 26 | "_index" : "website", 27 | "_type" : "blog", 28 | "_id" : "123", 29 | "_version" : 4 30 | } 31 | ``` 32 | 33 | 尽管文档不存在——`"found"`的值是`false`——`_version`依旧增加了。这是内部记录的一部分,它确保在多节点间不同操作可以有正确的顺序。 34 | 35 | > 正如在《更新文档》一章中提到的,删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。 36 | 37 | -------------------------------------------------------------------------------- /030_Data/60_Conclusion.md: -------------------------------------------------------------------------------- 1 | ## 结语 2 | 3 | 现在你知道如何把Elasticsearch当作一个分布式的文件存储了。你可以存储、更新、检索和删除它们,而且你知道如何安全的进行这一切。这确实非常非常有用,尽管我们还没有看到更多令人激动的特性,例如如何在文档内搜索。但让我们首先讨论下如何在分布式环境中安全的管理你的文档相关的内部流程。 4 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 分布式文档存储 2 | 3 | 在上一章,我们看到了将数据放入索引然后检索它们的所有方法。不过我们有意略过了许多关于数据是如何在集群中分布和获取的相关技术细节。这种使用和细节分离是刻意为之的——你不需要知道数据在Elasticsearch如何分布它就会很好的工作。 4 | 5 | 这一章我们深入这些内部细节来帮助你更好的理解数据是如何在分布式系统中存储的。 6 | 7 | > 注意: 8 | 9 | > 下面的信息只是出于兴趣阅读,你不必为了使用Elasticsearch而弄懂和记住所有的细节。讨论的这些选项只提供给高级用户。 10 | 11 | > 阅读这一部分只是让你了解下系统如何工作,并让你知道这些信息以备以后参考,所以不要被细节吓到。 12 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/05_Routing.md: -------------------------------------------------------------------------------- 1 | ## 路由文档到分片 2 | 3 | 当你索引一个文档,它被存储在单独一个主分片上。Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢? 4 | 5 | 进程不能是随机的,因为我们将来要检索文档。事实上,它根据一个简单的算法决定: 6 | 7 | shard = hash(routing) % number_of_primary_shards 8 | 9 | `routing`值是一个任意字符串,它默认是`_id`但也可以自定义。这个`routing`字符串通过哈希函数生成一个数字,然后除以主切片的数量得到一个**余数(remainder)**,余数的范围永远是`0`到`number_of_primary_shards - 1`,这个数字就是特定文档所在的分片。 10 | 11 | 这也解释了为什么主分片的数量只能在创建索引时定义且不能修改:如果主分片的数量在未来改变了,所有先前的路由值就失效了,文档也就永远找不到了。 12 | 13 | > 有时用户认为固定数量的主分片会让之后的扩展变得很困难。现实中,有些技术会在你需要的时候让扩展变得容易。我们将在《扩展》章节讨论。 14 | 15 | 所有的文档API(`get`、`index`、`delete`、`bulk`、`update`、`mget`)都接收一个`routing`参数,它用来自定义文档到分片的映射。自定义路由值可以确保所有相关文档——例如属于同一个人的文档——被保存在同一分片上。我们将在《扩展》章节说明你为什么需要这么做。 16 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/10_Shard_interaction.md: -------------------------------------------------------------------------------- 1 | ## 主分片和复制分片如何交互 2 | 3 | 为了阐述意图,我们假设有三个节点的集群。它包含一个叫做`bblogs`的索引并拥有两个主分片。每个主分片有两个复制分片。相同的分片不会放在同一个节点上,所以我们的集群是这样的: 4 | 5 | ![有三个节点一个索引的集群](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0401.png) 6 | 7 | 我们能够发送请求给集群中任意一个节点。每个节点都有能力处理任意请求。每个节点都知道任意文档所在的节点,所以也可以将请求转发到需要的节点。下面的例子中,我们将发送所有请求给`Node 1`,这个节点我们将会称之为**请求节点(requesting node)** 8 | 9 | > ### 提示: 10 | 11 | > 当我们发送请求,最好的做法是循环通过所有节点请求,这样可以平衡负载。 12 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/15_Create_index_delete.md: -------------------------------------------------------------------------------- 1 | ## 新建、索引和删除文档 2 | 3 | 新建、索引和删除请求都是**写(write)**操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。 4 | 5 | ![新建、索引或删除单一文档](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0402.png) 6 | 7 | 下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤: 8 | 9 | 1. 客户端给`Node 1`发送新建、索引或删除请求。 10 | 2. 节点使用文档的`_id`确定文档属于分片`0`。它转发请求到`Node 3`,分片`0`位于这个节点上。 11 | 3. `Node 3`在主分片上执行请求,如果成功,它转发请求到相应的位于`Node 1`和`Node 2`的复制节点上。当所有的复制节点报告成功,`Node 3`报告成功到请求的节点,请求的节点再报告给客户端。 12 | 13 | 客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了。 14 | 15 | 有很多可选的请求参数允许你更改这一过程。你可能想牺牲一些安全来提高性能。这一选项很少使用因为Elasticsearch已经足够快,不过为了内容的完整我们将做一些阐述。 16 | 17 | ### `replication` 18 | 19 | 复制默认的值是`sync`。这将导致主分片得到复制分片的成功响应后才返回。 20 | 21 | 如果你设置`replication`为`async`,请求在主分片上被执行后就会返回给客户端。它依旧会转发请求给复制节点,但你将不知道复制节点成功与否。 22 | 23 | 上面的这个选项不建议使用。默认的`sync`复制允许Elasticsearch强制反馈传输。`async`复制可能会因为在不等待其它分片就绪的情况下发送过多的请求而使Elasticsearch过载。 24 | 25 | ### `consistency` 26 | 27 | 默认主分片在尝试写入时需要**规定数量(quorum)**或过半的分片(可以是主节点或复制节点)可用。这是防止数据被写入到错的网络分区。规定的数量计算公式如下: 28 | 29 | int( (primary + number_of_replicas) / 2 ) + 1 30 | 31 | `consistency`允许的值为`one`(只有一个主分片),`all`(所有主分片和复制分片)或者默认的`quorum`或过半分片。 32 | 33 | 注意`number_of_replicas`是在索引中的的设置,用来定义复制分片的数量,而不是现在活动的复制节点的数量。如果你定义了索引有3个复制节点,那规定数量是: 34 | 35 | int( (primary + 3 replicas) / 2 ) + 1 = 3 36 | 37 | 但如果你只有2个节点,那你的活动分片不够规定数量,也就不能索引或删除任何文档。 38 | 39 | 40 | ### `timeout` 41 | 42 | 当分片副本不足时会怎样?Elasticsearch会等待更多的分片出现。默认等待一分钟。如果需要,你可以设置`timeout`参数让它终止的更早:`100`表示100毫秒,`30s`表示30秒。 43 | 44 | > 注意: 45 | 46 | > 新索引默认有`1`个复制分片,这意味着为了满足`quorum`的要求**需要**两个活动的分片。当然,这个默认设置将阻止我们在单一节点集群中进行操作。为了避开这个问题,规定数量只有在`number_of_replicas`大于一时才生效。 47 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/20_Retrieving.md: -------------------------------------------------------------------------------- 1 | ## 检索文档 2 | 3 | 文档能够从主分片或任意一个复制分片被检索。 4 | 5 | ![检索一个文档](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0403.png) 6 | 7 | 下面我们罗列在主分片或复制分片上检索一个文档必要的顺序步骤: 8 | 9 | 1. 客户端给`Node 1`发送get请求。 10 | 2. 节点使用文档的`_id`确定文档属于分片`0`。分片`0`对应的复制分片在三个节点上都有。此时,它转发请求到`Node 2`。 11 | 3. `Node 2`返回文档(document)给`Node 1`然后返回给客户端。 12 | 13 | 对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——它会循环所有分片副本。 14 | 15 | 可能的情况是,一个被索引的文档已经存在于主分片上却还没来得及同步到复制分片上。这时复制分片会报告文档未找到,主分片会成功返回文档。一旦索引请求成功返回给用户,文档则在主分片和复制分片都是可用的。 16 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/25_Partial_updates.md: -------------------------------------------------------------------------------- 1 | ## 局部更新文档 2 | 3 | `update` API 结合了之前提到的读和写的模式。 4 | 5 | ![局部更新文档](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0404.png) 6 | 7 | 下面我们罗列执行局部更新必要的顺序步骤: 8 | 9 | 1. 客户端给`Node 1`发送更新请求。 10 | 2. 它转发请求到主分片所在节点`Node 3`。 11 | 3. `Node 3`从主分片检索出文档,修改`_source`字段的JSON,然后在主分片上重建索引。如果有其他进程修改了文档,它以`retry_on_conflict`设置的次数重复步骤3,都未成功则放弃。 12 | 4. 如果`Node 3`成功更新文档,它同时转发文档的新版本到`Node 1`和`Node 2`上的复制节点以重建索引。当所有复制节点报告成功,`Node 3`返回成功给请求节点,然后返回给客户端。 13 | 14 | `update` API还接受《新建、索引和删除》章节提到的`routing`、`replication`、`consistency`和`timout`参数。 15 | 16 | > ### 基于文档的复制 17 | 18 | > 当主分片转发更改给复制分片时,并不是转发更新请求,而是转发整个文档的新版本。记住这些修改转发到复制节点是异步的,它们并不能保证到达的顺序与发送相同。如果Elasticsearch转发的仅仅是修改请求,修改的顺序可能是错误的,那得到的就是个损坏的文档。 19 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/30_Bulk_requests.md: -------------------------------------------------------------------------------- 1 | ## 多文档模式 2 | 3 | `mget`和`bulk` API与单独的文档类似。差别是请求节点知道每个文档所在的分片。它把多文档请求拆成**每个分片**的对文档请求,然后转发每个参与的节点。 4 | 5 | 一旦接收到每个节点的应答,然后整理这些响应组合为一个单独的响应,最后返回给客户端。 6 | 7 | ![通过mget检索多个文档](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0405.png) 8 | 9 | 下面我们将罗列通过一个`mget`请求检索多个文档的顺序步骤: 10 | 11 | 1. 客户端向`Node 1`发送`mget`请求。 12 | 2. `Node 1`为每个分片构建一个多条数据检索请求,然后转发到这些请求所需的主分片或复制分片上。当所有回复被接收,`Node 1`构建响应并返回给客户端。 13 | 14 | `routing` 参数可以被`docs`中的每个文档设置。 15 | 16 | ![通过打包批量修改文档](https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/master/images/elas_0406.png) 17 | 18 | 下面我们将罗列使用一个`bulk`执行多个`create`、`index`、`delete`和`update`请求的顺序步骤: 19 | 20 | 1. 客户端向`Node 1`发送`bulk`请求。 21 | 2. `Node 1`为每个分片构建批量请求,然后转发到这些请求所需的主分片上。 22 | 3. 主分片一个接一个的按序执行操作。当一个操作执行完,主分片转发新文档(或者删除部分)给对应的复制节点,然后执行下一个操作。一旦所有复制节点报告所有操作已成功完成,节点就报告success给请求节点,后者(请求节点)整理响应并返回给客户端。 23 | 24 | `bulk` API还可以在最上层使用`replication`和`consistency`参数,`routing`参数则在每个请求的元数据中使用。 25 | 26 | 27 | -------------------------------------------------------------------------------- /040_Distributed_CRUD/35_Bulk_format.md: -------------------------------------------------------------------------------- 1 | ## 为什么是奇怪的格式? 2 | 3 | 当我们在《批量》一章中学习了批量请求后,你可能会问:“为什么`bulk` API需要带换行符的奇怪格式,而不是像`mget` API一样使用JSON数组?” 4 | 5 | 为了回答这个问题,我们需要简单的介绍一下背景: 6 | 7 | 批量中每个引用的文档属于不同的主分片,每个分片可能被分布于集群中的某个节点上。这意味着批量中的每个**操作(action)**需要被转发到对应的分片和节点上。 8 | 9 | 如果每个单独的请求被包装到JSON数组中,那意味着我们需要: 10 | 11 | * 解析JSON为数组(包括文档数据,可能非常大) 12 | * 检查每个请求决定应该到哪个分片上 13 | * 为每个分片创建一个请求的数组 14 | * 序列化这些数组为内部传输格式 15 | * 发送请求到每个分片 16 | 17 | 这可行,但需要大量的RAM来承载本质上相同的数据,还要创建更多的数据结构使得JVM花更多的时间执行垃圾回收。 18 | 19 | 取而代之的,Elasticsearch则是从网络缓冲区中一行一行的直接读取数据。它使用换行符识别和解析**action/metadata**行,以决定哪些分片来处理这个请求。 20 | 21 | 这些行请求直接转发到对应的分片上。这些没有冗余复制,没有多余的数据结构。整个请求过程使用最小的内存在进行。 22 | 23 | -------------------------------------------------------------------------------- /050_Search/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 搜索——基本的工具 2 | 3 | 到目前为止,我们已经学会了如何使用elasticsearch作为一个简单的NoSQL风格的分布式文件存储器——我们可以将一个JSON文档扔给Elasticsearch,也可以根据ID检索它们。但Elasticsearch真正强大之处在于可以从混乱的数据中找出有意义的信息——从大数据到全面的信息。 4 | 5 | 这也是为什么我们使用结构化的JSON文档,而不是无结构的二进制数据。Elasticsearch不只会**存储(store)**文档,也会**索引(indexes)**文档内容来使之可以被搜索。 6 | 7 | **每个文档里的字段都会被索引并被查询**。而且不仅如此。在简单查询时,Elasticsearch可以使用**所有**的索引,以非常快的速度返回结果。这让你永远不必考虑传统数据库的一些东西。 8 | 9 | A _search_ can be: 10 | **搜索(search)**可以: 11 | 12 | * 在类似于`gender`或者`age`这样的字段上使用结构化查询,`join_date`这样的字段上使用排序,就像SQL的结构化查询一样。 13 | * 全文检索,可以使用所有字段来匹配关键字,然后按照**关联性(relevance)**排序返回结果。 14 | * 或者结合以上两条。 15 | 16 | 很多搜索都是开箱即用的,为了充分挖掘Elasticsearch的潜力,你需要理解以下三个概念: 17 | 18 | 19 | | 概念 | 解释 | 20 | | ------------------------------- | ----------------------------------------- | 21 | | **映射(Mapping)** | 数据在每个字段中的解释说明 | 22 | | **分析(Analysis)** | 全文是如何处理的可以被搜索的 | 23 | | **领域特定语言查询(Query DSL)** | Elasticsearch使用的灵活的、强大的查询语言 | 24 | 25 | 26 | 以上提到的每个点都是一个巨大的话题,我们将在《深入搜索》一章阐述它们。本章节我们将介绍这三点的一些基本概念——仅仅帮助你大致了解搜索是如何工作的。 27 | 28 | 我们将使用最简单的形式开始介绍`search` API. 29 | 30 | > ### 测试数据 31 | 32 | > 本章节测试用的数据可以在这里被找到[https://gist.github.com/clintongormley/8579281](https://gist.github.com/clintongormley/8579281) 33 | 34 | > 你可以把这些命令复制到终端中执行以便可以实践本章的例子。 35 | -------------------------------------------------------------------------------- /050_Search/05_Empty_search.md: -------------------------------------------------------------------------------- 1 | #空搜索 2 | 3 | 最基本的搜索API表单是**空搜索(empty search)**,它没有指定任何的查询条件,只返回集群索引中的所有文档: 4 | ```Javascript 5 | GET /_search 6 | ``` 7 | 8 | 响应内容(为了编辑简洁)类似于这样: 9 | 10 | ```Javascript 11 | { 12 | "hits" : { 13 | "total" : 14, 14 | "hits" : [ 15 | { 16 | "_index": "us", 17 | "_type": "tweet", 18 | "_id": "7", 19 | "_score": 1, 20 | "_source": { 21 | "date": "2014-09-17", 22 | "name": "John Smith", 23 | "tweet": "The Query DSL is really powerful and flexible", 24 | "user_id": 2 25 | } 26 | }, 27 | ... 9 RESULTS REMOVED ... 28 | ], 29 | "max_score" : 1 30 | }, 31 | "took" : 4, 32 | "_shards" : { 33 | "failed" : 0, 34 | "successful" : 10, 35 | "total" : 10 36 | }, 37 | "timed_out" : false 38 | } 39 | ``` 40 | 41 | ## `hits` 42 | 43 | 响应中最重要的部分是`hits`,它包含了`total`字段来表示匹配到的文档总数,`hits`数组还包含了匹配到的前10条数据。 44 | 45 | `hits`数组中的每个结果都包含`_index`、`_type`和文档的`_id`字段,被加入到`_source`字段中这意味着在搜索结果中我们将可以直接使用全部文档。这不像其他搜索引擎只返回文档ID,需要你单独去获取文档。 46 | 47 | 每个节点都有一个`_score`字段,这是**相关性得分(relevance score)**,它衡量了文档与查询的匹配程度。默认的,返回的结果中关联性最大的文档排在首位;这意味着,它是按照`_score`降序排列的。这种情况下,我们没有指定任何查询,所以所有文档的相关性是一样的,因此所有结果的`_score`都是取得一个中间值`1` 48 | 49 | `max_score`指的是所有文档匹配查询中`_score`的最大值。 50 | 51 | ## `took` 52 | 53 | `took`告诉我们整个搜索请求花费的毫秒数。 54 | 55 | ## `shards` 56 | 57 | `_shards`节点告诉我们参与查询的分片数(`total`字段),有多少是成功的(`successful`字段),有多少的是失败的(`failed`字段)。通常我们不希望分片失败,不过这个有可能发生。如果我们遭受一些重大的故障导致主分片和复制分片都故障,那这个分片的数据将无法响应给搜索请求。这种情况下,Elasticsearch将报告分片`failed`,但仍将继续返回剩余分片上的结果。 58 | 59 | ## `timeout` 60 | 61 | `time_out`值告诉我们查询超时与否。一般的,搜索请求不会超时。如果响应速度比完整的结果更重要,你可以定义`timeout`参数为`10`或者`10ms`(10毫秒),或者`1s`(1秒) 62 | 63 | 64 | ```javascript 65 | GET /_search?timeout=10ms 66 | ``` 67 | 68 | Elasticsearch将返回在请求超时前收集到的结果。 69 | 70 | 超时不是一个断路器(circuit breaker)(译者注:关于断路器的理解请看警告)。 71 | 72 | > ## 警告 73 | 74 | > 需要注意的是`timeout`不会停止执行查询,它仅仅告诉你**目前**顺利返回结果的节点然后关闭连接。在后台,其他分片可能依旧执行查询,尽管结果已经被发送。 75 | 76 | > 使用超时是因为对于你的业务需求(译者注:SLA,Service-Level Agreement服务等级协议,在此我翻译为业务需求)来说非常重要,而不是因为你想中断执行长时间运行的查询。 77 | -------------------------------------------------------------------------------- /050_Search/10_Multi_index_multi_type.md: -------------------------------------------------------------------------------- 1 | ## 多索引和多类别 2 | 3 | 你注意到空搜索的结果中不同类型的文档——`user`和`tweet`——来自于不同的索引——`us`和`gb`。 4 | 5 | 通过限制搜索的不同索引或类型,我们可以在集群中跨**所有**文档搜索。Elasticsearch转发搜索请求到集群中平行的主分片或每个分片的复制分片上,收集结果后选择顶部十个返回给我们。 6 | 7 | 通常,当然,你可能想搜索一个或几个自定的索引或类型,我们能通过定义URL中的索引或类型达到这个目的,像这样: 8 | 9 | #### `/_search` 10 | 在所有索引的所有类型中搜索 11 | 12 | #### `/gb/_search` 13 | 在索引`gb`的所有类型中搜索 14 | 15 | #### `/gb,us/_search` 16 | 在索引`gb`和`us`的所有类型中搜索 17 | 18 | #### `/g*,u*/_search` 19 | 在以`g`或`u`开头的索引的所有类型中搜索 20 | 21 | #### `/gb/user/_search` 22 | 在索引`gb`的类型`user`中搜索 23 | 24 | #### `/gb,us/user,tweet/_search` 25 | 在索引`gb`和`us`的类型为`user`和`tweet`中搜索 26 | 27 | #### `/_all/user,tweet/_search` 28 | 在所有索引的`user`和`tweet`中搜索 29 | search types `user` and `tweet` in all indices 30 | 31 | 当你搜索包含单一索引时,Elasticsearch转发搜索请求到这个索引的主分片或每个分片的复制分片上,然后聚集每个分片的结果。搜索包含多个索引也是同样的方式——只不过或有更多的分片被关联。 32 | 33 | > ## 重要 34 | 35 | > 搜索一个索引有5个主分片和5个索引各有一个分片**事实上是一样的**。 36 | 37 | 接下来,你将看到这些简单的情况如何灵活的扩展以适应你需求的变更。 38 | -------------------------------------------------------------------------------- /050_Search/15_Pagination.md: -------------------------------------------------------------------------------- 1 | ## 分页 2 | 3 | 《空搜索》一节告诉我们在集群中有14个文档匹配我们的(空)搜索语句。但是只有10个文档在`hits`数组中。我们如何看到其他文档? 4 | 5 | 和SQL使用`LIMIT`关键字返回只有一页的结果一样,Elasticsearch接受`from`和`size`参数: 6 | 7 | `size`: 结果数,默认`10` 8 | 9 | `from`: 跳过开始的结果数,默认`0` 10 | 11 | 如果你想每页显示5个结果,页码从1到3,那请求如下: 12 | 13 | ```javascript 14 | GET /_search?size=5 15 | GET /_search?size=5&from=5 16 | GET /_search?size=5&from=10 17 | ``` 18 | 19 | 应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。 20 | 21 | > ### 在集群系统中深度分页 22 | 23 | > 为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给**请求节点(requesting node)**,它再排序这所有的50个结果以选出顶端的10个结果。 24 | 25 | > 现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个! 26 | 27 | > 你可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。 28 | 29 | > ### TIP 30 | 31 | >在《重建索引》章节我们将阐述如何能高效的检索大量文档 32 | -------------------------------------------------------------------------------- /050_Search/20_Query_string.md: -------------------------------------------------------------------------------- 1 | ## 简易搜索 2 | 3 | `search` API有两种表单:一种是“简易版”的**查询字符串(query string)**将所有参数通过查询字符串定义,另一种版本使用JSON完整的表示**请求体(request body)**,这种富搜索语言叫做结构化查询语句(DSL) 4 | 5 | 查询字符串搜索对于在命令行下运行**点对点(ad hoc)**查询特别有用。例如这个语句查询所有类型为`tweet`并在`tweet`字段中包含`elasticsearch`字符的文档: 6 | 7 | ```javascript 8 | GET /_all/tweet/_search?q=tweet:elasticsearch 9 | ``` 10 | 11 | 下一个语句查找`name`字段中包含`"john"`和`tweet`字段包含`"mary"`的结果。实际的查询只需要: 12 | 13 | +name:john +tweet:mary 14 | 15 | 但是**百分比编码(percent encoding)**(译者注:就是url编码)需要将查询字符串参数变得更加神秘: 16 | 17 | ```Javascript 18 | GET /_search?q=%2Bname%3Ajohn+%2Btweet%3Amary 19 | ``` 20 | 21 | `"+"`前缀表示语句匹配条件**必须**被满足。类似的`"-"`前缀表示条件**必须不**被满足。所有条件如果没有`+`或`-`表示是可选的——匹配越多,相关的文档就越多。 22 | 23 | ### `_all`字段 24 | 25 | 返回包含`"mary"`字符的所有文档的简单搜索: 26 | 27 | ```javascript 28 | GET /_search?q=mary 29 | ``` 30 | 31 | 在前一个例子中,我们搜索`tweet`或`name`字段中包含某个字符的结果。然而,这个语句返回的结果在三个不同的字段中包含`"mary"`: 32 | 33 | * 用户的名字是“Mary” 34 | * “Mary”发的六个推文 35 | * 针对“@mary”的一个推文 36 | 37 | Elasticsearch是如何设法找到三个不同字段的结果的? 38 | 39 | 当你索引一个文档,Elasticsearch把所有字符串字段值连接起来放在一个大字符串中,它被索引为一个特殊的字段`_all`。例如,当索引这个文档: 40 | 41 | ```javascript 42 | { 43 | "tweet": "However did I manage before Elasticsearch?", 44 | "date": "2014-09-14", 45 | "name": "Mary Jones", 46 | "user_id": 1 47 | } 48 | ``` 49 | 50 | 这好比我们增加了一个叫做`_all`的额外字段值: 51 | 52 | ```javascript 53 | "However did I manage before Elasticsearch? 2014-09-14 Mary Jones 1" 54 | ``` 55 | 56 | 若没有指定字段,查询字符串搜索(即q=xxx)使用`_all`字段搜索。 57 | 58 | > ### TIP 59 | > `_all`字段对于开始一个新应用时是一个有用的特性。之后,如果你定义字段来代替`_all`字段,你的搜索结果将更加可控。当`_all`字段不再使用,你可以停用它,这个会在《全字段》章节阐述。 60 | 61 | ### 更复杂的语句 62 | 63 | 下一个搜索推特的语句: 64 | 65 | `_all` field 66 | * `name`字段包含`"mary"`或`"john"` 67 | * `date`晚于`2014-09-10` 68 | * `_all`字段包含`"aggregations"`或`"geo"` 69 | 70 | ```javascript 71 | +name:(mary john) +date:>2014-09-10 +(aggregations geo) 72 | ``` 73 | 74 | 编码后的查询字符串变得不太容易阅读: 75 | 76 | ```javascript 77 | ?q=%2Bname%3A(mary+john)+%2Bdate%3A%3E2014-09-10+%2B(aggregations+geo) 78 | ``` 79 | 80 | 就像你上面看到的例子,**简单(lite)**查询字符串搜索惊人的强大。它的查询语法,会在《查询字符串语法》章节阐述。参考文档允许我们简洁明快的表示复杂的查询。这对于命令行下一次性查询或者开发模式下非常有用。 81 | 82 | 然而,你可以看到简洁带来了隐晦和调试困难。而且它很脆弱——查询字符串中一个细小的语法错误,像`-`、`:`、`/`或`"`错位就会导致返回错误而不是结果。 83 | 84 | 最后,查询字符串搜索允许任意用户在索引中任何一个字段上运行潜在的慢查询语句,可能暴露私有信息甚至使你的集群瘫痪。 85 | 86 | > ### TIP 87 | > 因为这些原因,我们不建议直接暴露查询字符串搜索给用户,除非这些用户对于你的数据和集群可信。 88 | 89 | 取而代之的,生产环境我们一般依赖全功能的**请求体**搜索API,它能完成前面所有的事情,甚至更多。在了解它们之前,我们首先需要看看数据是如何在Elasticsearch中被索引的。 90 | -------------------------------------------------------------------------------- /052_Mapping_Analysis/00_Intro.md: -------------------------------------------------------------------------------- 1 | **映射(mapping)**机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型(`string`, `number`, `booleans`, `date`等)。 2 | 3 | **分析(analysis)**机制用于进行**全文文本(Full Text)**的分词,以建立供搜索用的反向索引。 4 | -------------------------------------------------------------------------------- /052_Mapping_Analysis/25_Data_type_differences.md: -------------------------------------------------------------------------------- 1 | ## 映射及分析 2 | 3 | 当在索引中处理数据时,我们注意到一些奇怪的事。有些东西似乎被破坏了: 4 | 5 | 在索引中有12个tweets,只有一个包含日期`2014-09-15`,但是我们看看下面查询中的`total` hits。 6 | 7 | ```javascript 8 | GET /_search?q=2014 # 12 个结果 9 | GET /_search?q=2014-09-15 # 还是 12 个结果 ! 10 | GET /_search?q=date:2014-09-15 # 1 一个结果 11 | GET /_search?q=date:2014 # 0 个结果 ! 12 | ``` 13 | 14 | 为什么全日期的查询返回所有的tweets,而针对`date`字段进行年度查询却什么都不返回? 15 | 为什么我们的结果因查询`_all`字段(译者注:默认所有字段中进行查询)或`date`字段而变得不同? 16 | 17 | 想必是因为我们的数据在`_all`字段的索引方式和在`date`字段的索引方式不同而导致。 18 | 19 | 让我们看看Elasticsearch在对`gb`索引中的`tweet`类型进行_mapping_(也称之为_模式定义_[注:此词有待重新定义(schema definition)])后是如何解读我们的文档结构: 20 | 21 | ```javascript 22 | GET /gb/_mapping/tweet 23 | ``` 24 | 25 | 返回: 26 | 27 | ```javascript 28 | { 29 | "gb": { 30 | "mappings": { 31 | "tweet": { 32 | "properties": { 33 | "date": { 34 | "type": "date", 35 | "format": "dateOptionalTime" 36 | }, 37 | "name": { 38 | "type": "string" 39 | }, 40 | "tweet": { 41 | "type": "string" 42 | }, 43 | "user_id": { 44 | "type": "long" 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | Elasticsearch为对字段类型进行猜测,动态生成了字段和类型的映射关系。返回的信息显示了`date`字段被识别为`date`类型。`_all`因为是默认字段所以没有在此显示,不过我们知道它是`string`类型。 54 | 55 | `date`类型的字段和`string`类型的字段的索引方式是不同的,因此导致查询结果的不同,这并不会让我们觉得惊讶。 56 | 57 | 你会期望每一种核心数据类型(strings, numbers, booleans及dates)以不同的方式进行索引,而这点也是现实:在Elasticsearch中他们是被区别对待的。 58 | 59 | 但是更大的区别在于_确切值_(exact values)(比如`string`类型)及_全文文本_(full text)之间。 60 | 61 | 这两者的区别才真的很重要 - 这是区分搜索引擎和其他数据库的根本差异。 62 | -------------------------------------------------------------------------------- /052_Mapping_Analysis/30_Exact_vs_full_text.md: -------------------------------------------------------------------------------- 1 | ## 确切值(Exact values) vs. 全文文本(Full text) 2 | 3 | Elasticsearch中的数据可以大致分为两种类型: 4 | 5 | _确切值_ 及 _全文文本_。 6 | 7 | 确切值是确定的,正如它的名字一样。比如一个date或用户ID,也可以包含更多的字符串比如username或email地址。 8 | 9 | 确切值`"Foo"`和`"foo"`就并不相同。确切值`2014`和`2014-09-15`也不相同。 10 | 11 | 全文文本,从另一个角度来说是文本化的数据(常常以人类的语言书写),比如一篇推文(Twitter的文章)或邮件正文。 12 | 13 | **** 14 | 15 | 全文文本常常被称为`非结构化数据`,其实是一种用词不当的称谓,实际上自然语言是高度结构化的。 16 | 17 | 问题是自然语言的语法规则是如此的复杂,计算机难以正确解析。例如这个句子: 18 | 19 | May is fun but June bores me. 20 | 21 | 到底是说的月份还是人呢? 22 | 23 | **** 24 | 25 | 确切值是很容易查询的,因为结果是二进制的 -- 要么匹配,要么不匹配。下面的查询很容易以SQL表达: 26 | 27 | ```javascript 28 | WHERE name = "John Smith" 29 | AND user_id = 2 30 | AND date > "2014-09-15" 31 | ``` 32 | 33 | 而对于全文数据的查询来说,却有些微妙。我们不会去询问`这篇文档是否匹配查询要求?`。 34 | 但是,我们会询问`这篇文档和查询的匹配程度如何?`。换句话说,对于查询条件,这篇文档的_相关性_有多高? 35 | 36 | 我们很少确切的匹配整个全文文本。我们想在全文中查询*包含*查询文本的部分。不仅如此,我们还期望搜索引擎能理解我们的*意图*: 37 | 38 | * 一个针对`"UK"`的查询将返回涉及`"United Kingdom"`的文档 39 | 40 | * 一个针对`"jump"`的查询同时能够匹配`"jumped"`, `"jumps"`, `"jumping"`甚至`"leap"` 41 | 42 | * `"johnny walker"`也能匹配`"Johnnie Walker"`, `"johnnie depp"`及`"Johnny Depp"` 43 | 44 | * `"fox news hunting"`能返回有关hunting on Fox News的故事,而`"fox hunting news"`也能返回关于fox hunting的新闻故事。 45 | 46 | 为了方便在全文文本字段中进行这些类型的查询,Elasticsearch首先对文本**分析(analyzes)**,然后使用结果建立一个**倒排索引**。我们将在以下两个章节讨论倒排索引及分析过程。 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /052_Mapping_Analysis/35_Inverted_index.md: -------------------------------------------------------------------------------- 1 | ## 倒排索引 2 | 3 | Elasticsearch使用一种叫做**倒排索引(inverted index)**的结构来做快速的全文搜索。倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。 4 | 5 | 例如,我们有两个文档,每个文档`content`字段包含: 6 | 7 | 1. The quick brown fox jumped over the lazy dog 8 | 2. Quick brown foxes leap over lazy dogs in summer 9 | 10 | 为了创建倒排索引,我们首先切分每个文档的`content`字段为单独的单词(我们把它们叫做**词(terms)**或者**表征(tokens)**)(译者注:关于`terms`和`tokens`的翻译比较生硬,只需知道语句分词后的个体叫做这两个。),把所有的唯一词放入列表并排序,结果是这个样子的: 11 | 12 | |Term | Doc_1 |Doc_2| 13 | |--------|-------|-----| 14 | |Quick | | X | 15 | |The | X | | 16 | |brown | X | X | 17 | |dog | X | | 18 | |dogs | | X | 19 | |fox | X | | 20 | |foxes | | X | 21 | |in | | X | 22 | |jumped | X | | 23 | |lazy | X | X | 24 | |leap | | X | 25 | |over | X | X | 26 | |quick | X | | 27 | |summer | | X | 28 | |the | X | || 29 | 30 | 现在,如果我们想搜索`"quick brown"`,我们只需要找到每个词在哪个文档中出现即可: 31 | 32 | 33 | |Term | Doc_1 |Doc_2| 34 | |-----|-------|-----| 35 | |brown| X | X | 36 | |quick| X | | 37 | |-----|-------|-----| 38 | |Total| 2 | 1 | 39 | 40 | 两个文档都匹配,但是第一个比第二个有更多的匹配项。 41 | 如果我们加入简单的**相似度算法(similarity algorithm)**,计算匹配单词的数目,这样我们就可以说第一个文档比第二个匹配度更高——对于我们的查询具有更多相关性。 42 | 43 | 但是在我们的倒排索引中还有些问题: 44 | 45 | 1. `"Quick"`和`"quick"`被认为是不同的单词,但是用户可能认为它们是相同的。 46 | 2. `"fox"`和`"foxes"`很相似,就像`"dog"`和`"dogs"`——它们都是同根词。 47 | 3. `"jumped"`和`"leap"`不是同根词,但意思相似——它们是同义词。 48 | 49 | 上面的索引中,搜索`"+Quick +fox"`不会匹配任何文档(记住,前缀`+`表示单词必须匹配到)。只有`"Quick"`和`"fox"`都在同一文档中才可以匹配查询,但是第一个文档包含`"quick fox"`且第二个文档包含`"Quick foxes"`。(译者注:这段真啰嗦,说白了就是单复数和同义词没法匹配) 50 | 51 | 用户可以合理地希望两个文档都能匹配查询,我们也可以做得更好。 52 | 53 | 如果我们将词为统一为标准格式,这样就可以找到不是确切匹配查询,但是足以相似从而可以关联的文档。例如: 54 | 55 | 1. `"Quick"`可以转为小写成为`"quick"`。 56 | 2. `"foxes"`可以被转为根形式`"fox"`。同理`"dogs"`可以被转为`"dog"`。 57 | 3. `"jumped"`和`"leap"`同义就可以只索引为单个词`"jump"` 58 | 59 | 现在的索引: 60 | 61 | |Term | Doc_1 |Doc_2| 62 | |--------|-------|-----| 63 | |brown | X | X | 64 | |dog | X | X | 65 | |fox | X | X | 66 | |in | | X | 67 | |jump | X | X | 68 | |lazy | X | X | 69 | |over | X | X | 70 | |quick | X | X | 71 | |summer | | X | 72 | |the | X | X | 73 | 74 | 但我们还未成功。我们的搜索`"+Quick +fox"`*依旧*失败,因为`"Quick"`的确切值已经不在索引里,不过,如果我们使用相同的标准化规则处理查询字符串的`content`字段,查询将变成`"+quick +fox"`,这样就可以匹配到两个文档。 75 | 76 | >### IMPORTANT 77 | >这很重要。你只可以找到确实存在于索引中的词,所以**索引文本和查询字符串都要标准化为相同的形式**。 78 | 79 | 这个标记化和标准化的过程叫做**分词(analysis)**,这个在下节中我们讨论。 80 | -------------------------------------------------------------------------------- /054_Query_DSL/00_Intro.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /054_Query_DSL/55_Request_body_search.md: -------------------------------------------------------------------------------- 1 | ## 请求体查询 2 | 3 | 简单查询语句(lite)是一种有效的命令行_adhoc_查询。但是,如果你想要善用搜索,你必须使用请求体查询(request body `search`)API。之所以这么称呼,是因为大多数的参数以JSON格式所容纳而非查询字符串。 4 | 5 | 请求体查询(下文简称查询),并不仅仅用来处理查询,而且还可以高亮返回结果中的片段,并且给出帮助你的用户找寻最好结果的相关数据建议。 6 | 7 | ### 空查询 8 | 9 | 我们以最简单的 `search` API开始,空查询将会返回索引中所有的文档。 10 | 11 | ```Javascript 12 | GET /_search 13 | {} <1> 14 | ``` 15 | 16 | - <1> 这是一个空查询数据。 17 | 18 | 同字符串查询一样,你可以查询一个,多个或`_all`索引(indices)或类型(types): 19 | 20 | ```Javascript 21 | GET /index_2014*/type1,type2/_search 22 | {} 23 | ``` 24 | 25 | 你可以使用`from`及`size`参数进行分页: 26 | 27 | ```Javascript 28 | GET /_search 29 | { 30 | "from": 30, 31 | "size": 10 32 | } 33 | ``` 34 | 35 | 36 | ************************************************* 37 | 携带内容的`GET`请求? 38 | 39 | 任何一种语言(特别是js)的HTTP库都不允许`GET`请求中携带交互数据。 40 | 事实上,有些用户很惊讶`GET`请求中居然会允许携带交互数据。 41 | 42 | 真实情况是,http://tools.ietf.org/html/rfc7231#page-24[RFC 7231], 43 | 一份规定HTTP语义及内容的RFC中并未规定`GET`请求中允许携带交互数据! 44 | 所以,有些HTTP服务允许这种行为,而另一些(特别是缓存代理),则不允许这种行为。 45 | 46 | Elasticsearch的作者们倾向于使用`GET`提交查询请求,因为他们觉得这个词相比`POST`来说,能更好的描述这种行为。 47 | 然而,因为携带交互数据的`GET`请求并不被广泛支持,所以`search`API同样支持`POST`请求,类似于这样: 48 | ```Javascript 49 | POST /_search 50 | { 51 | "from": 30, 52 | "size": 10 53 | } 54 | ``` 55 | 56 | 这个原理同样应用于其他携带交互数据的`GET`API请求中。 57 | 58 | ************************************************* 59 | 60 | 我们将在后续的章节中讨论聚合查询,但是现在我们把关注点仅放在查询语义上。 61 | 62 | 相对于神秘的查询字符串方法,请求体查询允许我们使用结构化查询Query DSL(Query Domain Specific Language) 63 | -------------------------------------------------------------------------------- /054_Query_DSL/65_Queries_vs_filters.md: -------------------------------------------------------------------------------- 1 | translate by [williamzhao](https://github.com/williamzhao) 2 | 3 | ## 查询与过滤 4 | 5 | 前面我们讲到的是关于结构化查询语句,事实上我们可以使用两种结构化语句: 6 | 结构化查询(Query DSL)和结构化过滤(Filter DSL)。 7 | 查询与过滤语句非常相似,但是它们由于使用目的不同而稍有差异。 8 | 9 | 一条过滤语句会询问每个文档的字段值是否包含着特定值: 10 | 11 | * `created` 的日期范围是否在 `2013` 到 `2014` ? 12 | 13 | * `status` 字段中是否包含单词 "published" ? 14 | 15 | * `lat_lon` 字段中的地理位置与目标点相距是否不超过10km ? 16 | 17 | 一条查询语句与过滤语句相似,但问法不同: 18 | 19 | 查询语句会询问每个文档的字段值与特定值的匹配程度如何? 20 | 21 | 查询语句的典型用法是为了找到文档: 22 | 23 | * 查找与 `full text search` 这个词语最佳匹配的文档 24 | 25 | * 查找包含单词 `run` ,但是也包含`runs`, `running`, `jog` 或 `sprint`的文档 26 | 27 | * 同时包含着 `quick`, `brown` 和 `fox` --- 单词间离得越近,该文档的相关性越高 28 | 29 | * 标识着 `lucene`, `search` 或 `java` --- 标识词越多,该文档的相关性越高 30 | 31 | 一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 `_score`,并且 32 | 按照相关性对匹配到的文档进行排序。 33 | 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。 34 | 35 | ## 性能差异 36 | 37 | 使用过滤语句得到的结果集 -- 一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 38 | 每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。 39 | 40 | 查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 41 | 过滤语句更耗时,并且查询结果也不可缓存。 42 | 43 | 幸亏有了倒排索引,一个只匹配少量文档的简单查询语句在百万级文档中的查询效率会与一条经过缓存 44 | 的过滤语句旗鼓相当,甚至略占上风。 45 | 但是一般情况下,一条经过缓存的过滤查询要远胜一条查询语句的执行效率。 46 | 47 | 过滤语句的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。 48 | 49 | ## 什么情况下使用 50 | 51 | 原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句 52 | 53 | -------------------------------------------------------------------------------- /054_Query_DSL/80_Validating_queries.md: -------------------------------------------------------------------------------- 1 | ## 验证查询 2 | 3 | 查询语句可以变得非常复杂,特别是与不同的分析器和字段映射相结合后,就会有些难度。 4 | 5 | `validate` API 可以验证一条查询语句是否合法。 6 | 7 | 8 | ```Javascript 9 | GET /gb/tweet/_validate/query 10 | { 11 | "query": { 12 | "tweet" : { 13 | "match" : "really powerful" 14 | } 15 | } 16 | } 17 | ``` 18 | 19 | 以上请求的返回值告诉我们这条语句是非法的: 20 | 21 | ```Javascript 22 | { 23 | "valid" : false, 24 | "_shards" : { 25 | "total" : 1, 26 | "successful" : 1, 27 | "failed" : 0 28 | } 29 | } 30 | ``` 31 | 32 | ## 理解错误信息 33 | 34 | 想知道语句非法的具体错误信息,需要加上 `explain` 参数: 35 | 36 | ```Javascript 37 | GET /gb/tweet/_validate/query?explain <1> 38 | { 39 | "query": { 40 | "tweet" : { 41 | "match" : "really powerful" 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | <1> `explain` 参数可以提供语句错误的更多详情。 48 | 49 | 很显然,我们把 query 语句的 `match` 与字段名位置弄反了: 50 | 51 | ```Javascript 52 | { 53 | "valid" : false, 54 | "_shards" : { ... }, 55 | "explanations" : [ { 56 | "index" : "gb", 57 | "valid" : false, 58 | "error" : "org.elasticsearch.index.query.QueryParsingException: 59 | [gb] No query registered for [tweet]" 60 | } ] 61 | } 62 | ``` 63 | 64 | ## 理解查询语句 65 | 66 | 如果是合法语句的话,使用 `explain` 参数可以返回一个带有查询语句的可阅读描述, 67 | 可以帮助了解查询语句在ES中是如何执行的: 68 | 69 | ```Javascript 70 | GET /_validate/query?explain 71 | { 72 | "query": { 73 | "match" : { 74 | "tweet" : "really powerful" 75 | } 76 | } 77 | } 78 | ``` 79 | 80 | `explanation` 会为每一个索引返回一段描述,因为每个索引会有不同的映射关系和分析器: 81 | 82 | ```Javascript 83 | { 84 | "valid" : true, 85 | "_shards" : { ... }, 86 | "explanations" : [ { 87 | "index" : "us", 88 | "valid" : true, 89 | "explanation" : "tweet:really tweet:powerful" 90 | }, { 91 | "index" : "gb", 92 | "valid" : true, 93 | "explanation" : "tweet:really tweet:power" 94 | } ] 95 | } 96 | ``` 97 | 98 | 从返回的 `explanation` 你会看到 `match` 是如何为查询字符串 `"really powerful"` 进行查询的, 99 | 首先,它被拆分成两个独立的词分别在 `tweet` 字段中进行查询。 100 | 101 | 而且,在索引`us`中这两个词为`"really"`和`"powerful"`,在索引`gb`中被拆分成`"really"` 和 `"power"`。 102 | 这是因为我们在索引`gb`中使用了`english`分析器。 103 | -------------------------------------------------------------------------------- /054_Query_DSL/85_Conclusion.md: -------------------------------------------------------------------------------- 1 | ## 结语 2 | 3 | 这一章详细介绍了如何在项目中使用常见的查询语句。 4 | 5 | 也就是说,想要完全掌握搜索和结构化查询,还需要在工作中花费大量的时间来理解ES的工作方式。 6 | 7 | 更高级的部分,我们将会在《深入搜索》中详细讲解,但是在讲解之前,你还需要理解查询结果是如何进行排序的, 8 | 9 | 下一章我们将学习如何根据相关性对查询结果进行排序以及指定排序过程。 10 | -------------------------------------------------------------------------------- /056_Sorting/00_Intro.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /056_Sorting/88_String_sorting.md: -------------------------------------------------------------------------------- 1 | ## 多值字段字符串排序 2 | 3 | > 译者注: `多值字段`是指同一个字段在ES索引中可以有多个含义,即可使用多个分析器(analyser)进行分词与排序,也可以不添加分析器,保留原值。 4 | 5 | 被分析器(analyser)处理过的字符称为`analyzed field`(译者注:即已被分词并排序的字段,所有写入ES中的字段默认圴会被analyzed), `analyzed`字符串字段同时也是多值字段,在这些字段上排序往往得不到你想要的值。 6 | 比如你分析一个字符 `"fine old art"`,它最终会得到三个值。例如我们想要按照第一个词首字母排序, 7 | 如果第一个单词相同的话,再用第二个词的首字母排序,以此类推,可惜 ElasticSearch 在进行排序时 8 | 是得不到这些信息的。 9 | 10 | 当然你可以使用 `min` 和 `max` 模式来排(默认使用的是 `min` 模式)但它是依据`art` 或者 `old`排序, 11 | 而不是我们所期望的那样。 12 | 13 | 为了使一个string字段可以进行排序,它必须只包含一个词:即完整的`not_analyzed`字符串(译者注:未经分析器分词并排序的原字符串)。 14 | 当然我们需要对字段进行全文本搜索的时候还必须使用被 `analyzed` 标记的字段。 15 | 16 | 在 `_source` 下相同的字符串上排序两次会造成不必要的资源浪费。 17 | 而我们想要的是同一个字段中同时包含这两种索引方式,我们只需要改变索引(index)的mapping即可。 18 | 方法是在所有核心字段类型上,使用通用参数 `fields`对mapping进行修改。 19 | 比如,我们原有mapping如下: 20 | 21 | ```Javascript 22 | "tweet": { 23 | "type": "string", 24 | "analyzer": "english" 25 | } 26 | ``` 27 | 28 | 改变后的多值字段mapping如下: 29 | 30 | ```Javascript 31 | "tweet": { <1> 32 | "type": "string", 33 | "analyzer": "english", 34 | "fields": { 35 | "raw": { <2> 36 | "type": "string", 37 | "index": "not_analyzed" 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | <1> `tweet` 字段用于全文本的 `analyzed` 索引方式不变。 44 | <2> 新增的 `tweet.raw` 子字段索引方式是 `not_analyzed`。 45 | 46 | 现在,在给数据重建索引后,我们既可以使用 `tweet` 字段进行全文本搜索,也可以用`tweet.raw`字段进行排序: 47 | 48 | ```Javascript 49 | GET /_search 50 | { 51 | "query": { 52 | "match": { 53 | "tweet": "elasticsearch" 54 | } 55 | }, 56 | "sort": "tweet.raw" 57 | } 58 | ``` 59 | 60 | >**警告**: 61 | >对 `analyzed` 字段进行强制排序会消耗大量内存。 62 | >详情请查阅《字段类型简介》相关内容。 63 | -------------------------------------------------------------------------------- /056_Sorting/95_Fielddata.md: -------------------------------------------------------------------------------- 1 | ## 数据字段 2 | 3 | 本章的目的在于介绍关于ElasticSearch内部的一些运行情况。在这里我们先不介绍新的知识点, 4 | 数据字段是我们要经常查阅的内容之一,但我们使用的时候不必太在意。 5 | 6 | 当你对一个字段进行排序时,ElasticSearch 需要进入每个匹配到的文档得到相关的值。 7 | 倒排索引在用于搜索时是非常卓越的,但却不是理想的排序结构。 8 | 9 | * 当搜索的时候,我们需要用检索词去遍历所有的文档。 10 | 11 | * 当排序的时候,我们需要遍历文档中所有的值,我们需要做反倒序排列操作。 12 | 13 | 为了提高排序效率,ElasticSearch 会将所有字段的值加载到内存中,这就叫做"数据字段"。 14 | 15 | >**重要**: 16 | >ElasticSearch将所有字段数据加载到内存中并不是匹配到的那部分数据。 17 | >而是索引下所有文档中的值,包括所有类型。 18 | 19 | 将所有字段数据加载到内存中是因为从硬盘反向倒排索引是非常缓慢的。尽管你这次请求需要的是某些文档中的部分数据, 20 | 但你下个请求却需要另外的数据,所以将所有字段数据一次性加载到内存中是十分必要的。 21 | 22 | ElasticSearch中的字段数据常被应用到以下场景: 23 | 24 | * 对一个字段进行排序 25 | * 对一个字段进行聚合 26 | * 某些过滤,比如地理位置过滤 27 | * 某些与字段相关的脚本计算 28 | 29 | 毫无疑问,这会消耗掉很多内存,尤其是大量的字符串数据 -- string字段可能包含很多不同的值,比如邮件内容。 30 | 值得庆幸的是,内存不足是可以通过横向扩展解决的,我们可以增加更多的节点到集群。 31 | 32 | 现在,你只需要知道字段数据是什么,和什么时候内存不足就可以了。 33 | 稍后我们会讲述字段数据到底消耗了多少内存,如何限制ElasticSearch可以使用的内存,以及如何预加载字段数据以提高用户体验。 34 | -------------------------------------------------------------------------------- /060_Distributed_Search/00_Intro.md: -------------------------------------------------------------------------------- 1 | #分布式搜索的执行方式 2 | 在继续之前,我们将绕道讲一下搜索是如何在分布式环境中执行的。 它比我们之前讲的基础的_增删改查_(_create-read-update-delete_ ,CRUD)请求要复杂一些。 3 | 4 | > ####注意: 5 | 6 | > 本章的信息只是出于兴趣阅读,使用Elasticsearch并不需要理解和记住这里的所有细节。 7 | 8 | > 阅读这一章只是增加对系统如何工作的了解,并让你知道这些信息以备以后参考,所以别淹没在细节里。 9 | 10 | 一个CRUD操作只处理一个单独的文档。文档的唯一性由`_index`, `_type`和`routing-value`(通常默认是该文档的`_id`)的组合来确定。这意味着我们可以准确知道集群中的哪个分片持有这个文档。 11 | 12 | 由于不知道哪个文档会匹配查询(文档可能存放在集群中的任意分片上),所以搜索需要一个更复杂的模型。一个搜索不得不通过查询每一个我们感兴趣的索引的分片副本,来看是否含有任何匹配的文档。 13 | 14 | 但是,找到所有匹配的文档只完成了这件事的一半。在搜索(`search`)API返回一页结果前,来自多个分片的结果必须被组合放到一个有序列表中。因此,搜索的执行过程分两个阶段,称为_查询然后取回_(_query then fetch_)。 15 | 16 | 54 | -------------------------------------------------------------------------------- /070_Index_Mgmt/00_Intro.md: -------------------------------------------------------------------------------- 1 | ##索引管理 2 | 我们已经看到Elasticsearch如何在不需要任何预先计划和设置的情况下,轻松地开发一个新的应用。并且,在你想调整索引和搜索过程来更好地适应你特殊的使用需求前,不会花较长的时间。它包含几乎所有的和索引及类型相关的定制选项。在这一章,将介绍管理索引和类型映射的API以及最重要的设置。 3 | -------------------------------------------------------------------------------- /070_Index_Mgmt/05_Create_Delete.md: -------------------------------------------------------------------------------- 1 | ### 创建索引 2 | 3 | 迄今为止,我们简单的通过添加一个文档的方式创建了一个索引。这个索引使用默认设置,新的属性通过动态映射添加到分类中。现在我们需要对这个过程有更多的控制:我们需要确保索引被创建在适当数量的分片上,在索引数据_之前_设置好分析器和类型映射。 4 | 5 | 为了达到目标,我们需要手动创建索引,在请求中加入所有设置和类型映射,如下所示: 6 | 7 | ``` 8 | PUT /my_index 9 | { 10 | "settings": { ... any settings ... }, 11 | "mappings": { 12 | "type_one": { ... any mappings ... }, 13 | "type_two": { ... any mappings ... }, 14 | ... 15 | } 16 | ``` 17 | 18 | 事实上,你可以通过在 `config/elasticsearch.yml` 中添加下面的配置来防止自动创建索引。 19 | 20 | ```yml 21 | action.auto_create_index: false 22 | ``` 23 | 24 | > **NOTE** 25 | 26 | > 今后,我们将介绍怎样用【索引模板】来自动预先配置索引。这在索引日志数据时尤其有效: 27 | > 你将日志数据索引在一个以日期结尾的索引上,第二天,一个新的配置好的索引会自动创建好。 28 | 29 | ### 删除索引 30 | 31 | 使用以下的请求来删除索引: 32 | 33 | ``` 34 | DELETE /my_index 35 | ``` 36 | 37 | 你也可以用下面的方式删除多个索引 38 | 39 | ``` 40 | DELETE /index_one,index_two 41 | DELETE /index_* 42 | ``` 43 | 44 | 你甚至可以删除所有索引 45 | 46 | ``` 47 | DELETE /_all 48 | ``` 49 | -------------------------------------------------------------------------------- /070_Index_Mgmt/10_Settings.md: -------------------------------------------------------------------------------- 1 | ### 索引设置 2 | 3 | 你可以通过很多种方式来自定义索引行为,你可以阅读[Index Modules reference documentation](http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/_index_settings.html#_index_settings),但是: 4 | 5 | 提示: Elasticsearch 提供了优化好的默认配置。除非你明白这些配置的行为和为什么要这么做,请不要修改这些配置。 6 | 7 | 下面是两个最重要的设置: 8 | 9 | `number_of_shards` 10 | 11 | 定义一个索引的主分片个数,默认值是 `5`。这个配置在索引创建后不能修改。 12 | 13 | `number_of_replicas` 14 | 15 | 每个主分片的复制分片个数,默认是 `1`。这个配置可以随时在活跃的索引上修改。 16 | 17 | 例如,我们可以创建只有一个主分片,没有复制分片的小索引。 18 | 19 | ``` 20 | PUT /my_temp_index 21 | { 22 | "settings": { 23 | "number_of_shards" : 1, 24 | "number_of_replicas" : 0 25 | } 26 | } 27 | ``` 28 | 29 | 30 | 31 | 然后,我们可以用 `update-index-settings` API 动态修改复制分片个数: 32 | 33 | ``` 34 | PUT /my_temp_index/_settings 35 | { 36 | "number_of_replicas": 1 37 | } 38 | ``` 39 | 40 | 41 | -------------------------------------------------------------------------------- /070_Index_Mgmt/15_Configure_Analyzer.md: -------------------------------------------------------------------------------- 1 | ### 配置分析器 2 | 3 | 第三个重要的索引设置是 `analysis` 部分,用来配置已存在的分析器或创建自定义分析器来定制化你的索引。 4 | 5 | 在【分析器介绍】中,我们介绍了一些内置的分析器,用于将全文字符串转换为适合搜索的倒排索引。 6 | 7 | `standard` 分析器是用于全文字段的默认分析器,对于大部分西方语系来说是一个不错的选择。它考虑了以下几点: 8 | 9 | * `standard` 分词器,在词层级上分割输入的文本。 10 | * `standard` 标记过滤器,被设计用来整理分词器触发的所有标记(但是目前什么都没做)。 11 | * `lowercase` 标记过滤器,将所有标记转换为小写。 12 | * `stop` 标记过滤器,删除所有可能会造成搜索歧义的停用词,如 `a`,`the`,`and`,`is`。 13 | 14 | 默认情况下,停用词过滤器是被禁用的。如需启用它,你可以通过创建一个基于 `standard` 分析器的自定义分析器,并且设置 `stopwords` 参数。可以提供一个停用词列表,或者使用一个特定语言的预定停用词列表。 15 | 16 | 在下面的例子中,我们创建了一个新的分析器,叫做 `es_std`,并使用预定义的西班牙语停用词: 17 | 18 | ``` 19 | PUT /spanish_docs 20 | { 21 | "settings": { 22 | "analysis": { 23 | "analyzer": { 24 | "es_std": { 25 | "type": "standard", 26 | "stopwords": "_spanish_" 27 | } 28 | } 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | 35 | 36 | `es_std` 分析器不是全局的,它仅仅存在于我们定义的 `spanish_docs` 索引中。为了用 `analyze` API 来测试它,我们需要使用特定的索引名。 37 | 38 | ``` 39 | GET /spanish_docs/_analyze?analyzer=es_std 40 | El veloz zorro marrón 41 | ``` 42 | 43 | 44 | 45 | 下面简化的结果中显示停用词 `El` 被正确的删除了: 46 | 47 | ``` 48 | { 49 | "tokens" : [ 50 | { "token" : "veloz", "position" : 2 }, 51 | { "token" : "zorro", "position" : 3 }, 52 | { "token" : "marrón", "position" : 4 } 53 | ] 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /070_Index_Mgmt/30_Root_Object.md: -------------------------------------------------------------------------------- 1 | ### 根对象 2 | 3 | 映射的最高一层被称为 _根对象_,它可能包含下面几项: 4 | 5 | * 一个 _properties_ 节点,列出了文档中可能包含的每个字段的映射 6 | 7 | * 多个元数据字段,每一个都以下划线开头,例如 `_type`, `_id` 和 `_source` 8 | 9 | * 设置项,控制如何动态处理新的字段,例如 `analyzer`, `dynamic_date_formats` 和 `dynamic_templates`。 10 | 11 | * 其他设置,可以同时应用在根对象和其他 `object` 类型的字段上,例如 `enabled`, `dynamic` 和 `include_in_all` 12 | 13 | ### 属性 14 | 15 | 我们已经在【核心字段】和【复合核心字段】章节中介绍过文档字段和属性的三个最重要的设置: 16 | 17 | `type`: 18 | 字段的数据类型,例如 `string` 和 `date` 19 | 20 | `index`: 21 | 字段是否应当被当成全文来搜索(`analyzed`),或被当成一个准确的值(`not_analyzed`),还是完全不可被搜索(`no`) 22 | 23 | `analyzer`: 24 | 确定在索引和或搜索时全文字段使用的 `分析器`。 25 | 26 | 我们将在下面的章节中介绍其他字段,例如 `ip`, `geo_point` 和 `geo_shape` 27 | -------------------------------------------------------------------------------- /070_Index_Mgmt/31_Metadata_source.md: -------------------------------------------------------------------------------- 1 | ### 元数据:_source 字段 2 | 3 | 默认情况下,Elasticsearch 用 JSON 字符串来表示文档主体保存在 `_source` 字段中。像其他保存的字段一样,`_source` 字段也会在写入硬盘前压缩。 4 | 5 | 这几乎始终是需要的功能,因为: 6 | 7 | * 搜索结果中能得到完整的文档 —— 不需要额外去别的数据源中查询文档 8 | 9 | * 如果缺少 `_source` 字段,部分 `更新` 请求不会起作用 10 | 11 | * 当你的映射有变化,而且你需要重新索引数据时,你可以直接在 Elasticsearch 中操作而不需要重新从别的数据源中取回数据。 12 | 13 | * 你可以从 `_source` 中通过 `get` 或 `search` 请求取回部分字段,而不是整个文档。 14 | 15 | * 这样更容易排查错误,因为你可以准确的看到每个文档中包含的内容,而不是只能从一堆 ID 中猜测他们的内容。 16 | 17 | 即便如此,存储 `_source` 字段还是要占用硬盘空间的。假如上面的理由对你来说不重要,你可以用下面的映射禁用 `_source` 字段: 18 | 19 | ``` 20 | PUT /my_index 21 | { 22 | "mappings": { 23 | "my_type": { 24 | "_source": { 25 | "enabled": false 26 | } 27 | } 28 | } 29 | } 30 | ``` 31 | 32 | 在搜索请求中你可以通过限定 `_source` 字段来请求指定字段: 33 | 34 | ``` 35 | GET /_search 36 | { 37 | "query": { "match_all": {}}, 38 | "_source": [ "title", "created" ] 39 | } 40 | ``` 41 | 42 | 43 | 44 | 这些字段会从 `_source` 中提取出来,而不是返回整个 `_source` 字段。 45 | 46 | > 储存字段 47 | 48 | > 除了索引字段的值,你也可以选择 `储存` 字段的原始值以备日后取回。使用 Lucene 做后端的用户用_储存字段_来选择搜索结果的返回值,事实上,`_source` 字段就是一个储存字段。 49 | 50 | > 在 Elasticsearch 中,单独设置储存字段不是一个好做法。完整的文档已经被保存在 `_source` 字段中。通常最好的办法会是使用 `_source` 参数来过滤你需要的字段。 51 | -------------------------------------------------------------------------------- /070_Index_Mgmt/32_Metadata_all.md: -------------------------------------------------------------------------------- 1 | ### 元数据:_all 字段 2 | 3 | 在【简单搜索】中,我们介绍了 `_all` 字段:一个所有其他字段值的特殊字符串字段。`query_string` 在没有指定字段时默认用 `_all` 字段查询。 4 | 5 | `_all` 字段在新应用的探索阶段比较管用,当你还不清楚最终文档的结构时,可以将任何查询用于这个字段,就有机会得到你想要的文档: 6 | 7 | ``` 8 | GET /_search 9 | { 10 | "match": { 11 | "_all": "john smith marketing" 12 | } 13 | } 14 | ``` 15 | 16 | 随着你应用的发展,搜索需求会变得更加精准。你会越来越少的使用 `_all` 字段。`_all` 是一种简单粗暴的搜索方式。通过查询独立的字段,你能更灵活,强大和精准的控制搜索结果,提高相关性。 17 | 18 | **提示** 19 | 20 | 【相关性算法】考虑的一个最重要的原则是字段的长度:字段越短,就越重要。在较短的 `title` 字段中的短语会比较长的 `content` 字段中的短语显得更重要。而字段间的这种差异在 `_all` 字段中就不会出现 21 | 22 | 如果你决定不再使用 `_all` 字段,你可以通过下面的映射禁用它: 23 | 24 | ``` 25 | PUT /my_index/_mapping/my_type 26 | { 27 | "my_type": { 28 | "_all": { "enabled": false } 29 | } 30 | } 31 | ``` 32 | 33 | 通过 `include_in_all` 选项可以控制字段是否要被包含在 `_all` 字段中,默认值是 `true`。在一个对象上设置 `include_in_all` 可以修改这个对象所有字段的默认行为。 34 | 35 | 你可能想要保留 `_all` 字段来查询所有特定的全文字段,例如 `title`, `overview`, `summary` 和 `tags`。相对于完全禁用 `_all` 字段,你可以先默认禁用 `include_in_all` 选项,而选定字段上启用 `include_in_all`。 36 | 37 | ``` 38 | PUT /my_index/my_type/_mapping 39 | { 40 | "my_type": { 41 | "include_in_all": false, 42 | "properties": { 43 | "title": { 44 | "type": "string", 45 | "include_in_all": true 46 | }, 47 | ... 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | 谨记 `_all` 字段仅仅是一个经过分析的 `string` 字段。它使用默认的分析器来分析它的值,而不管这值本来所在的字段指定的分析器。而且像所有 `string` 类型字段一样,你可以配置 `_all` 字段使用的分析器: 54 | 55 | ``` 56 | PUT /my_index/my_type/_mapping 57 | { 58 | "my_type": { 59 | "_all": { "analyzer": "whitespace" } 60 | } 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /070_Index_Mgmt/33_Metadata_ID.md: -------------------------------------------------------------------------------- 1 | ### 文档 ID 2 | 3 | 文档唯一标识由四个元数据字段组成: 4 | 5 | `_id`:文档的字符串 ID 6 | 7 | `_type`:文档的类型名 8 | 9 | `_index`:文档所在的索引 10 | 11 | `_uid`:`_type` 和 `_id` 连接成的 `type#id` 12 | 13 | 默认情况下,`_uid` 是被保存(可取回)和索引(可搜索)的。`_type` 字段被索引但是没有保存,`_id` 和 `_index` 字段则既没有索引也没有储存,它们并不是真实存在的。 14 | 15 | 尽管如此,你仍然可以像真实字段一样查询 `_id` 字段。Elasticsearch 使用 `_uid` 字段来追溯 `_id`。虽然你可以修改这些字段的 `index` 和 `store` 设置,但是基本上不需要这么做。 16 | 17 | `_id` 字段有一个你可能用得到的设置:`path` 设置告诉 Elasticsearch 它需要从文档本身的哪个字段中生成 `_id` 18 | 19 | ``` 20 | PUT /my_index 21 | { 22 | "mappings": { 23 | "my_type": { 24 | "_id": { 25 | "path": "doc_id" <1> 26 | }, 27 | "properties": { 28 | "doc_id": { 29 | "type": "string", 30 | "index": "not_analyzed" 31 | } 32 | } 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | 39 | 40 | <1> 从 `doc_id` 字段生成 `_id` 41 | 42 | 然后,当你索引一个文档时: 43 | 44 | ``` 45 | POST /my_index/my_type 46 | { 47 | "doc_id": "123" 48 | } 49 | ``` 50 | 51 | 52 | 53 | `_id` 值由文档主体的 `doc_id` 字段生成。 54 | 55 | ``` 56 | { 57 | "_index": "my_index", 58 | "_type": "my_type", 59 | "_id": "123", <1> 60 | "_version": 1, 61 | "created": true 62 | } 63 | ``` 64 | 65 | <1> `_id` 正确的生成了。 66 | 67 | 警告:虽然这样很方便,但是注意它对 `bulk` 请求(见【bulk 格式】)有个轻微的性能影响。处理请求的节点将不能仅靠解析元数据行来决定将请求分配给哪一个分片,而需要解析整个文档主体。 68 | -------------------------------------------------------------------------------- /070_Index_Mgmt/35_Dynamic_Mapping.md: -------------------------------------------------------------------------------- 1 | ### 动态映射 2 | 3 | 当 Elasticsearch 处理一个位置的字段时,它通过【动态映射】来确定字段的数据类型且自动将该字段加到类型映射中。 4 | 5 | 有时这是理想的行为,有时却不是。或许你不知道今后会有哪些字段加到文档中,但是你希望它们能自动被索引。或许你仅仅想忽略它们。特别是当你使用 Elasticsearch 作为主数据源时,你希望未知字段能抛出一个异常来警示你。 6 | 7 | 幸运的是,你可以通过 `dynamic` 设置来控制这些行为,它接受下面几个选项: 8 | 9 | `true`:自动添加字段(默认) 10 | 11 | `false`:忽略字段 12 | 13 | `strict`:当遇到未知字段时抛出异常 14 | 15 | `dynamic` 设置可以用在根对象或任何 `object` 对象上。你可以将 `dynamic` 默认设置为 `strict`,而在特定内部对象上启用它: 16 | 17 | ``` 18 | PUT /my_index 19 | { 20 | "mappings": { 21 | "my_type": { 22 | "dynamic": "strict", <1> 23 | "properties": { 24 | "title": { "type": "string"}, 25 | "stash": { 26 | "type": "object", 27 | "dynamic": true <2> 28 | } 29 | } 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | 36 | 37 | <1> 当遇到未知字段时,`my_type` 对象将会抛出异常 38 | 39 | <2> `stash` 对象会自动创建字段 40 | 41 | 通过这个映射,你可以添加一个新的可搜索字段到 `stash` 对象中: 42 | 43 | ``` 44 | PUT /my_index/my_type/1 45 | { 46 | "title": "This doc adds a new field", 47 | "stash": { "new_field": "Success!" } 48 | } 49 | ``` 50 | 51 | 52 | 53 | 但是在顶层做同样的操作则会失败: 54 | 55 | ``` 56 | PUT /my_index/my_type/1 57 | { 58 | "title": "This throws a StrictDynamicMappingException", 59 | "new_field": "Fail!" 60 | } 61 | ``` 62 | 63 | 64 | 65 | 备注:将 `dynamic` 设置成 `false` 完全不会修改 `_source` 字段的内容。`_source` 将仍旧保持你索引时的完整 JSON 文档。然而,没有被添加到映射的未知字段将不可被搜索。 66 | -------------------------------------------------------------------------------- /070_Index_Mgmt/45_Default_Mapping.md: -------------------------------------------------------------------------------- 1 | ### 默认映射 2 | 3 | 通常,一个索引中的所有类型具有共享的字段和设置。用 `_default_` 映射来指定公用设置会更加方便,而不是每次创建新的类型时重复操作。`_default` 映射像新类型的模板。所有在 `_default_` 映射 _之后_ 的类型将包含所有的默认设置,除非在自己的类型映射中明确覆盖这些配置。 4 | 5 | 例如,我们可以使用 `_default_` 映射对所有类型禁用 `_all` 字段,而只在 `blog` 字段上开启它: 6 | 7 | ``` 8 | PUT /my_index 9 | { 10 | "mappings": { 11 | "_default_": { 12 | "_all": { "enabled": false } 13 | }, 14 | "blog": { 15 | "_all": { "enabled": true } 16 | } 17 | } 18 | } 19 | ``` 20 | 21 | 22 | 23 | `_default_` 映射也是定义索引级别的动态模板的好地方。 24 | -------------------------------------------------------------------------------- /070_Index_Mgmt/50_Reindexing.md: -------------------------------------------------------------------------------- 1 | ### 重新索引数据 2 | 3 | 虽然你可以给索引添加新的类型,或给类型添加新的字段,但是你不能添加新的分析器或修改已有字段。假如你这样做,已被索引的数据会变得不正确而你的搜索也不会正常工作。 4 | 5 | 修改在已存在的数据最简单的方法是重新索引:创建一个新配置好的索引,然后将所有的文档从旧的索引复制到新的上。 6 | 7 | `_source` 字段的一个最大的好处是你已经在 Elasticsearch 中有了完整的文档,你不再需要从数据库中重建你的索引,这样通常会比较慢。 8 | 9 | 为了更高效的索引旧索引中的文档,使用【scan-scoll】来批量读取旧索引的文档,然后将通过【bulk API】来将它们推送给新的索引。 10 | 11 | 批量重新索引: 12 | 13 | 你可以在同一时间执行多个重新索引的任务,但是你显然不愿意它们的结果有重叠。所以,可以将重建大索引的任务通过日期或时间戳字段拆分成较小的任务: 14 | 15 | ``` 16 | GET /old_index/_search?search_type=scan&scroll=1m 17 | { 18 | "query": { 19 | "range": { 20 | "date": { 21 | "gte": "2014-01-01", 22 | "lt": "2014-02-01" 23 | } 24 | } 25 | }, 26 | "size": 1000 27 | } 28 | ``` 29 | 30 | 假如你继续在旧索引上做修改,你可能想确保新增的文档被加到了新的索引中。这可以通过重新运行重建索引程序来完成,但是记得只要过滤出上次执行后新增的文档就行了。 31 | -------------------------------------------------------------------------------- /070_Index_Mgmt/55_Aliases.md: -------------------------------------------------------------------------------- 1 | ### 索引别名和零停机时间 2 | 3 | 前面提到的重新索引过程中的问题是必须更新你的应用,来使用另一个索引名。索引别名正是用来解决这个问题的! 4 | 5 | 索引 _别名_ 就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何需要索引名的 API 使用。别名带给我们极大的灵活性,允许我们做到: 6 | 7 | * 在一个运行的集群上无缝的从一个索引切换到另一个 8 | * 给多个索引分类(例如,`last_three_months`) 9 | * 给索引的一个子集创建 `视图` 10 | 11 | 我们以后会讨论更多别名的使用场景。现在我们将介绍用它们怎么在零停机时间内从旧的索引切换到新的索引。 12 | 13 | 这里有两种管理别名的途径:`_alias` 用于单个操作,`_aliases` 用于原子化多个操作。 14 | 15 | 在这一章中,我们假设你的应用采用一个叫 `my_index` 的索引。而事实上,`my_index` 是一个指向当前真实索引的别名。真实的索引名将包含一个版本号:`my_index_v1`, `my_index_v2` 等等。 16 | 17 | 开始,我们创建一个索引 `my_index_v1`,然后将别名 `my_index` 指向它: 18 | 19 | ``` 20 | PUT /my_index_v1 <1> 21 | PUT /my_index_v1/_alias/my_index <2> 22 | ``` 23 | 24 | 25 | 26 | <1> 创建索引 `my_index_v1`。 27 | <2> 将别名 `my_index` 指向 `my_index_v1`。 28 | 29 | 你可以检测这个别名指向哪个索引: 30 | 31 | ``` 32 | GET /*/_alias/my_index 33 | ``` 34 | 35 | 36 | 37 | 或哪些别名指向这个索引: 38 | 39 | ``` 40 | GET /my_index_v1/_alias/* 41 | ``` 42 | 43 | 44 | 45 | 两者都将返回下列值: 46 | 47 | ``` 48 | { 49 | "my_index_v1" : { 50 | "aliases" : { 51 | "my_index" : { } 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | 然后,我们决定修改索引中一个字段的映射。当然我们不能修改现存的映射,索引我们需要重新索引数据。首先,我们创建有新的映射的索引 `my_index_v2`。 58 | 59 | ``` 60 | PUT /my_index_v2 61 | { 62 | "mappings": { 63 | "my_type": { 64 | "properties": { 65 | "tags": { 66 | "type": "string", 67 | "index": "not_analyzed" 68 | } 69 | } 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | 76 | 77 | 然后我们从将数据从 `my_index_v1` 迁移到 `my_index_v2`,下面的过程在【重新索引】中描述过了。一旦我们认为数据已经被正确的索引了,我们就将别名指向新的索引。 78 | 79 | 别名可以指向多个索引,所以我们需要在新索引中添加别名的同时从旧索引中删除它。这个操作需要原子化,所以我们需要用 `_aliases` 操作: 80 | 81 | ``` 82 | POST /_aliases 83 | { 84 | "actions": [ 85 | { "remove": { "index": "my_index_v1", "alias": "my_index" }}, 86 | { "add": { "index": "my_index_v2", "alias": "my_index" }} 87 | ] 88 | } 89 | ``` 90 | 91 | 92 | 93 | 这样,你的应用就从旧索引迁移到了新的,而没有停机时间。 94 | 95 | 提示: 96 | 97 | 即使你认为现在的索引设计已经是完美的了,当你的应用在生产环境使用时,还是有可能在今后有一些改变的。 98 | 99 | 所以请做好准备:在应用中使用别名而不是索引。然后你就可以在任何时候重建索引。别名的开销很小,应当广泛使用。 100 | -------------------------------------------------------------------------------- /075_Inside_a_shard/00_Intro.md: -------------------------------------------------------------------------------- 1 | # 入门 2 | 在[分布式集群](020_Distributed_Cluster/00_Intro.md)中,我们介绍了分片,把它描述为底层的工作单元。但分片到底是什么,它怎样工作?在这章节,我们将回答这些问题: 3 | * 为什么搜索是近实时的? 4 | * 为什么文档的CRUD操作是实时的? 5 | * ES怎样保证更新持久化,即使断电也不会丢失? 6 | * 为什么删除文档不会立即释放空间? 7 | * 什么是`refresh,flush, optimize API`,以及什么时候你该使用它们? 8 | 9 | 为了理解分片如何工作,最简单的方式是从一堂历史课开始。我们将会看下,为了提供一个有近实时搜索和分析功能的分布式、持久化的搜索引擎需要解决哪些问题。 10 | 11 | > 内容提示: 12 | 13 | > 这章的内容是为了满足你的兴趣。为了使用ES,你不需要懂得并记住所有细节。阅读这章是为了感受下ES内部是如何运转的以及相关信息在哪,以备不时之需。但是不要被这些细节吓到。 -------------------------------------------------------------------------------- /075_Inside_a_shard/20_Making_text_searchable.md: -------------------------------------------------------------------------------- 1 | #使文本可以被搜索 2 | 3 | 第一个不得不解决的挑战是如何让文本变得可搜索。在传统的数据库中,一个字段存一个值,但是这对于全文搜索是不足的。想要让文本中的每个单词都可以被搜索,这意味这数据库需要存多个值。 4 | 5 | 支持一个字段多个值的最佳数据结构是[倒排索引](052_Mapping_Analysis/35_Inverted_index.md)。倒排索引包含了出现在所有文档中唯一的值或词的有序列表,以及每个词所属的文档列表。 6 | 7 | 8 | Term | Doc 1 | Doc 2 | Doc 3 | ... 9 | ------------------------------------ 10 | brown | X | | X | ... 11 | fox | X | X | X | ... 12 | quick | X | X | | ... 13 | the | X | | X | ... 14 | 15 | 16 | > 注意 17 | 18 | >当讨论倒排索引时,我们说的是把文档加入索引。因为之前,一个倒排索引是用来索引整个非结构化的文本文档。ES中的文档是一个结构化的JSON文档。实际上,每一个JSON文档中被索引的字段都有它自己的倒排索引。 19 | 20 | 倒排索引存储了比包含了一个特定term的文档列表多地多的信息。它可能存储包含每个term的文档数量,一个term出现在指定文档中的频次,每个文档中term的顺序,每个文档的长度,所有文档的平均长度,等等。这些统计信息让Elasticsearch知道哪些term更重要,哪些文档更重要,也就是[相关性](056_Sorting/90_What_is_relevance.md)。 21 | 22 | 需要意识到,为了实现倒排索引预期的功能,它必须要知道集合中所有的文档。 23 | 24 | 在全文检索的早些时候,会为整个文档集合建立一个大索引,并且写入磁盘。只有新的索引准备好了,它就会替代旧的索引,最近的修改才可以被检索。 25 | 26 | ##不可变性 27 | 写入磁盘的倒排索引是不可变的,它有如下好处: 28 | * 不需要锁。如果从来不需要更新一个索引,就不必担心多个程序同时尝试修改。 29 | * 一旦索引被读入文件系统的缓存(译者:在内存),它就一直在那儿,因为不会改变。只要文件系统缓存有足够的空间,大部分的读会直接访问内存而不是磁盘。这有助于性能提升。 30 | * 在索引的声明周期内,所有的其他缓存都可用。它们不需要在每次数据变化了都重建,因为数据不会变。 31 | * 写入单个大的倒排索引,可以压缩数据,较少磁盘IO和需要缓存索引的内存大小。 32 | 33 | 当然,不可变的索引有它的缺点,首先是它不可变!你不能改变它。如果想要搜索一个新文档,必须重见整个索引。这不仅严重限制了一个索引所能装下的数据,还有一个索引可以被更新的频次。 34 | -------------------------------------------------------------------------------- /075_Inside_a_shard/30_Dynamic_indices.md: -------------------------------------------------------------------------------- 1 | #动态索引 2 | 3 | 下一个需要解决的问题是如何在保持不可变好处的同时更新倒排索引。答案是,使用多个索引。 4 | 5 | 不是重写整个倒排索引,而是增加额外的索引反映最近的变化。每个倒排索引都可以按顺序查询,从最老的开始,最后把结果聚合。 6 | 7 | Elasticsearch底层依赖的Lucene,引入了`per-segment search`的概念。一个段(segment)是有完整功能的倒排索引,但是现在Lucene中的索引指的是段的集合,再加上提交点(commit point,包括所有段的文件),如**图1**所示。新的文档,在被写入磁盘的段之前,首先写入内存区的索引缓存,如**图2、图3**所示。 8 | 9 | **图1:一个提交点和三个索引的Lucene** 10 | 11 | ![一个提交点和三个索引的Lucene](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1101.png) 12 | 13 | >索引vs分片 14 | 15 | >为了避免混淆,需要说明,Lucene索引是Elasticsearch中的分片,Elasticsearch中的索引是分片的集合。当Elasticsearch搜索索引时,它发送查询请求给该索引下的所有分片,然后过滤这些结果,聚合成全局的结果。 16 | 17 | 18 | 一个`per-segment search`如下工作: 19 | 1. 新的文档首先写入内存区的索引缓存。 20 | 2. 不时,这些buffer被提交: 21 | * 一个新的段——额外的倒排索引——写入磁盘。 22 | * 新的提交点写入磁盘,包括新段的名称。 23 | * 磁盘是fsync’ed(文件同步)——所有写操作等待文件系统缓存同步到磁盘,确保它们可以被物理写入。 24 | 3. 新段被打开,它包含的文档可以被检索 25 | 4. 内存的缓存被清除,等待接受新的文档。 26 | 27 | **图2:内存缓存区有即将提交文档的Lucene索引** 28 | 29 | ![内存缓存区有即将提交文档的Lucene索引](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1102.png) 30 | 31 | **图3:提交后,新的段加到了提交点,缓存被清空** 32 | 33 | ![提交后,新的段加到了提交点,缓存被清空](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1103.png) 34 | 35 | 当一个请求被接受,所有段依次查询。所有段上的Term统计信息被聚合,确保每个term和文档的相关性被正确计算。通过这种方式,新的文档以较小的代价加入索引。 36 | 37 | ##删除和更新 38 | 39 | 段是不可变的,所以文档既不能从旧的段中移除,旧的段也不能更新以反映文档最新的版本。相反,每一个提交点包括一个.del文件,包含了段上已经被删除的文档。 40 | 41 | 当一个文档被删除,它实际上只是在.del文件中被标记为删除,依然可以匹配查询,但是最终返回之前会被从结果中删除。 42 | 43 | 文档的更新操作是类似的:当一个文档被更新,旧版本的文档被标记为删除,新版本的文档在新的段中索引。也许该文档的不同版本都会匹配一个查询,但是更老版本会从结果中删除。 44 | 45 | 在[合并段](075_Inside_a_shard/60_Segment_merging.md)这节,我们会展示删除的文件是如何从文件系统中清除的。 -------------------------------------------------------------------------------- /075_Inside_a_shard/40_Near_real_time.md: -------------------------------------------------------------------------------- 1 | #近实时搜索 2 | 3 | 因为`per-segment search`机制,索引和搜索一个文档之间是有延迟的。新的文档会在几分钟内可以搜索,但是这依然不够快。 4 | 5 | 磁盘是瓶颈。提交一个新的段到磁盘需要`fsync`操作,确保段被物理地写入磁盘,即时电源失效也不会丢失数据。但是`fsync`是昂贵的,它不能在每个文档被索引的时就触发。 6 | 7 | 所以需要一种更轻量级的方式使新的文档可以被搜索,这意味这移除`fsync`。 8 | 9 | 位于Elasticsearch和磁盘间的是文件系统缓存。如前所说,在内存索引缓存中的文档(图1)被写入新的段(图2),但是新的段首先写入文件系统缓存,这代价很低,之后会被同步到磁盘,这个代价很大。但是一旦一个文件被缓存,它也可以被打开和读取,就像其他文件一样。 10 | 11 | **图1:内存缓存区有新文档的Lucene索引** 12 | ![内存缓存区有新文档的Lucene索引](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1104.png) 13 | 14 | Lucene允许新段写入打开,好让它们包括的文档可搜索,而不用执行一次全量提交。这是比提交更轻量的过程,可以经常操作,而不会影响性能。 15 | 16 | **图2:缓存内容已经写到段中,但是还没提交** 17 | ![缓存内容已经写到段中,但是还没提交](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1105.png) 18 | 19 | ##refeash API 20 | 在Elesticsearch中,这种写入打开一个新段的轻量级过程,叫做refresh。默认情况下,每个分片每秒自动刷新一次。这就是为什么说Elasticsearch是近实时的搜索了:文档的改动不会立即被搜索,但是会在一秒内可见。 21 | 22 | 这会困扰新用户:他们索引了个文档,尝试搜索它,但是搜不到。解决办法就是执行一次手动刷新,通过API: 23 | 24 | ```Javascript 25 | POST /_refresh <1> 26 | POST /blogs/_refresh <2> 27 | ``` 28 | - <1> refresh所有索引 29 | - <2> 只refresh 索引`blogs` 30 | 31 | >虽然刷新比提交更轻量,但是它依然有消耗。人工刷新在测试写的时有用,但是不要在生产环境中每写一次就执行刷新,这会影响性能。相反,你的应用需要意识到ES近实时搜索的本质,并且容忍它。 32 | 33 | 不是所有的用户都需要每秒刷新一次。也许你使用ES索引百万日志文件,你更想要优化索引的速度,而不是进实时搜索。你可以通过修改配置项`refresh_interval`减少刷新的频率: 34 | ```Javascript 35 | PUT /my_logs 36 | { 37 | "settings": { 38 | "refresh_interval": "30s" <1> 39 | } 40 | } 41 | ``` 42 | - <1> 每30s refresh一次`my_logs` 43 | 44 | `refresh_interval`可以在存在的索引上动态更新。你在创建大索引的时候可以关闭自动刷新,在要使用索引的时候再打开它。 45 | 46 | ```Javascript 47 | PUT /my_logs/_settings 48 | { "refresh_interval": -1 } <1> 49 | 50 | PUT /my_logs/_settings 51 | { "refresh_interval": "1s" } <2> 52 | ``` 53 | - <1> 禁用所有自动refresh 54 | - <2> 每秒自动refresh 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /075_Inside_a_shard/50_Persistent_changes.md: -------------------------------------------------------------------------------- 1 | #持久化变更 2 | 3 | 没用`fsync`同步文件系统缓存到磁盘,我们不能确保电源失效,甚至正常退出应用后,数据的安全。为了ES的可靠性,需要确保变更持久化到磁盘。 4 | 5 | 我们说过一次全提交同步段到磁盘,写提交点,这会列出所有的已知的段。在重启,或重新打开索引时,ES使用这次提交点决定哪些段属于当前的分片。 6 | 7 | 当我们通过每秒的刷新获得近实时的搜索,我们依然需要定时地执行全提交确保能从失败中恢复。但是提交之间的文档怎么办?我们也不想丢失它们。 8 | 9 | ES增加了事务日志(`translog`),来记录每次操作。有了事务日志,过程现在如下: 10 | 11 | 1. 当一个文档被索引,它被加入到内存缓存,同时加到事务日志。 12 | 13 | **图1:新的文档加入到内存缓存,同时写入事务日志** 14 | ![新的文档加入到内存缓存,同时写入事务日志](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1106.png) 15 | 2. refresh使得分片的进入如下图描述的状态。每秒分片都进行refeash: 16 | * 内存缓冲区的文档写入到段中,但没有fsync。 17 | * 段被打开,使得新的文档可以搜索。 18 | * 缓存被清除 19 | 20 | **图2:经过一次refresh,缓存被清除,但事务日志没有** 21 | ![经过一次refresh,缓存被清除,但事务日志没有](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1107.png) 22 | 23 | 3. 随着更多的文档加入到缓存区,写入日志,这个过程会继续 24 | 25 | **图3:事务日志会记录增长的文档** 26 | ![事务日志会记录增长的文档](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1108.png) 27 | 28 | 4. 不时地,比如日志很大了,新的日志会创建,会进行一次全提交: 29 | * 内存缓存区的所有文档会写入到新段中。 30 | * 清除缓存 31 | * 一个提交点写入硬盘 32 | * 文件系统缓存通过fsync操作flush到硬盘 33 | * 事务日志被清除 34 | 35 | 事务日志记录了没有flush到硬盘的所有操作。当故障重启后,ES会用最近一次提交点从硬盘恢复所有已知的段,并且从日志里恢复所有的操作。 36 | 37 | 事务日志还用来提供实时的CRUD操作。当你尝试用ID进行CRUD时,它在检索相关段内的文档前会首先检查日志最新的改动。这意味着ES可以实时地获取文档的最新版本。 38 | 39 | **图4:flush过后,段被全提交,事务日志清除** 40 | ![flush过后,段被全提交,事务日志清除](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1109.png) 41 | 42 | ##flush API 43 | 在ES中,进行一次提交并删除事务日志的操作叫做 `flush`。分片每30分钟,或事务日志过大会进行一次flush操作。 44 | 45 | `flush API`可用来进行一次手动flush: 46 | ```Javascript 47 | POST /blogs/_flush <1> 48 | 49 | POST /_flush?wait_for_ongoing <2> 50 | ``` 51 | - <1> flush索引`blogs` 52 | - <2> flush所有索引,等待操作结束再返回 53 | 54 | 你很少需要手动`flush`,通常自动的就够了。 55 | 56 | 当你要重启或关闭一个索引,flush该索引是很有用的。当ES尝试恢复或者重新打开一个索引时,它必须重放所有事务日志中的操作,所以日志越小,恢复速度越快。 57 | -------------------------------------------------------------------------------- /075_Inside_a_shard/60_Segment_merging.md: -------------------------------------------------------------------------------- 1 | #合并段 2 | 通过每秒自动刷新创建新的段,用不了多久段的数量就爆炸了。有太多的段是一个问题。每个段消费文件句柄,内存,cpu资源。更重要的是,每次搜索请求都需要依次检查每个段。段越多,查询越慢。 3 | 4 | ES通过后台合并段解决这个问题。小段被合并成大段,再合并成更大的段。 5 | 6 | 这是旧的文档从文件系统删除的时候。旧的段不会再复制到更大的新段中。 7 | 8 | 这个过程你不必做什么。当你在索引和搜索时ES会自动处理。这个过程如图:两个提交的段和一个未提交的段合并为了一个更大的段所示: 9 | 10 | 1. 索引过程中,refresh会创建新的段,并打开它。 11 | 2. 合并过程会在后台选择一些小的段合并成大的段,这个过程不会中断索引和搜索。 12 | 13 | **图1:两个提交的段和一个未提交的段合并为了一个更大的段** 14 | ![两个提交的段和一个未提交的段合并为了一个更大的段](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1110.png) 15 | 16 | 3. 下图描述了合并后的操作: 17 | * 新的段flush到了硬盘。 18 | * 新的提交点写入新的段,排除旧的段。 19 | * 新的段打开供搜索。 20 | * 旧的段被删除。 21 | 22 | **图2:段合并完后,旧的段被删除** 23 | ![段合并完后,旧的段被删除](https://www.elastic.co/guide/en/elasticsearch/guide/current/images/elas_1111.png) 24 | 25 | 合并大的段会消耗很多IO和CPU,如果不检查会影响到搜素性能。默认情况下,ES会限制合并过程,这样搜索就可以有足够的资源进行。 26 | 27 | ##optimize API 28 | `optimize API`最好描述为强制合并段API。它强制分片合并段以达到指定`max_num_segments`参数。这是为了减少段的数量(通常为1)达到提高搜索性能的目的。 29 | 30 | >警告 31 | 32 | >不要在动态的索引(正在活跃更新)上使用`optimize API`。后台的合并处理已经做的很好了,优化命令会阻碍它的工作。不要干涉! 33 | 34 | 在特定的环境下,`optimize API`是有用的。典型的场景是记录日志,这中情况下日志是按照每天,周,月存入索引。旧的索引一般是只可读的,它们是不可能修改的。 35 | 这种情况下,把每个索引的段降至1是有效的。搜索过程就会用到更少的资源,性能更好: 36 | ```Javascript 37 | POST /logstash-2014-10/_optimize?max_num_segments=1 <1> 38 | ``` 39 | - <1> 把索引中的每个分片都合并成一个段 40 | 41 | 42 | -------------------------------------------------------------------------------- /080_Structured_Search/00_structuredsearch.md: -------------------------------------------------------------------------------- 1 | ### 结构化搜索 2 | 3 | _结构化搜索_ 是指查询包含内部结构的数据。日期,时间,和数字都是结构化的:它们有明确的格式给你执行逻辑操作。一般包括比较数字或日期的范围,或确定两个值哪个大。 4 | 5 | 文本也可以被结构化。一包蜡笔有不同的颜色:`红色`,`绿色`,`蓝色`。一篇博客可能被打上 `分布式` 和 `搜索`的标签。电子商务产品有商品统一代码(UPCs) 或其他有着严格格式的标识。 6 | 7 | 通过结构化搜索,你的查询结果_始终_是 是或非;是否应该属于集合。结构化搜索不关心文档的相关性或分数,它只是简单的包含或排除文档。 8 | 9 | 这必须是有意义的逻辑,一个数字不能比同一个范围中的其他数字 _更多_。它只能包含在一个范围中 —— 或不在其中。类似的,对于结构化文本,一个值必须相等或不等。这里没有 _更匹配_ 的概念。 10 | -------------------------------------------------------------------------------- /080_Structured_Search/15_terms.md: -------------------------------------------------------------------------------- 1 | ### 查询多个准确值 2 | 3 | `term` 过滤器在查询单个值时很好用,但是你可能经常需要搜索多个值。比如你想寻找 20 或 30 元的文档,该怎么做呢? 4 | 5 | 比起使用多个 `term` 过滤器,你可以用一个 `terms` 过滤器。`terms` 过滤器是 `term` 过滤器的复数版本。 6 | 7 | 它用起来和 `term` 差不多,我们现在来指定一组数值,而不是单一价格: 8 | 9 | ```json 10 | { 11 | "terms" : { 12 | "price" : [20, 30] 13 | } 14 | } 15 | ``` 16 | 17 | 像 `term` 过滤器一样,我们将它放在 `filtered` 查询中: 18 | 19 | ```json 20 | GET /my_store/products/_search 21 | { 22 | "query" : { 23 | "filtered" : { 24 | "filter" : { 25 | "terms" : { <1> 26 | "price" : [20, 30] 27 | } 28 | } 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | 35 | 36 | <1> 这是前面提到的 `terms` 过滤器,放置在 `filtered` 查询中 37 | 38 | 这条查询将返回第二,第三和第四个文档: 39 | 40 | ```json 41 | "hits" : [ 42 | { 43 | "_id" : "2", 44 | "_score" : 1.0, 45 | "_source" : { 46 | "price" : 20, 47 | "productID" : "KDKE-B-9947-#kL5" 48 | } 49 | }, 50 | { 51 | "_id" : "3", 52 | "_score" : 1.0, 53 | "_source" : { 54 | "price" : 30, 55 | "productID" : "JODL-X-1937-#pV7" 56 | } 57 | }, 58 | { 59 | "_id": "4", 60 | "_score": 1.0, 61 | "_source": { 62 | "price": 30, 63 | "productID": "QQPX-R-3956-#aD8" 64 | } 65 | } 66 | ] 67 | ``` 68 | -------------------------------------------------------------------------------- /080_Structured_Search/20_contains.md: -------------------------------------------------------------------------------- 1 | ### 包含,而不是相等 2 | 3 | 理解 `term` 和 `terms` 是_包含_操作,而不是_相等_操作,这点非常重要。这意味着什么? 4 | 5 | 假如你有一个 term 过滤器 `{ "term" : { "tags" : "search" } }`,它将匹配下面两个文档: 6 | 7 | ```json 8 | { "tags" : ["search"] } 9 | { "tags" : ["search", "open_source"] } <1> 10 | ``` 11 | 12 | <1> 虽然这个文档除了 `search` 还有其他短语,它还是被返回了 13 | 14 | 回顾一下 `term` 过滤器是怎么工作的:它检查倒排索引中所有具有短语的文档,然后组成一个字节集。在我们简单的示例中,我们有下面的倒排索引: 15 | 16 | | Token | DocIDs | 17 | | ------------ | ------- | 18 | |`open_source` | `2` | 19 | |`search` | `1`,`2` | 20 | 21 | 当执行 `term` 过滤器来查询 `search` 时,它直接在倒排索引中匹配值并找出相关的 ID。如你所见,文档 1 和文档 2 都包含 `search`,所以他们都作为结果集返回。 22 | 23 | 提示: 24 | 倒排索引的特性让完全匹配一个字段变得非常困难。你将如何确定一个文档_只能_包含你请求的短语?你将在索引中找出这个短语,解出所有相关文档 ID,然后扫描 _索引中每一行_来确定文档是否包含其他值。 25 | 26 | 由此可见,这将变得非常低效和开销巨大。因此,`term` 和 `terms` 是 _必须包含_ 操作,而不是 _必须相等_。 27 | 28 | #### 完全匹配 29 | 30 | 假如你真的需要完全匹配这种行为,最好是通过添加另一个字段来实现。在这个字段中,你索引原字段包含值的个数。引用上面的两个文档,我们现在包含一个字段来记录标签的个数: 31 | 32 | ```json 33 | { "tags" : ["search"], "tag_count" : 1 } 34 | { "tags" : ["search", "open_source"], "tag_count" : 2 } 35 | ``` 36 | 37 | 38 | 39 | 一旦你索引了标签个数,你可以构造一个 `bool` 过滤器来限制短语个数: 40 | 41 | ```json 42 | GET /my_index/my_type/_search 43 | { 44 | "query": { 45 | "filtered" : { 46 | "filter" : { 47 | "bool" : { 48 | "must" : [ 49 | { "term" : { "tags" : "search" } }, <1> 50 | { "term" : { "tag_count" : 1 } } <2> 51 | ] 52 | } 53 | } 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 60 | 61 | <1> 找出所有包含 `search` 短语的文档 62 | <2> 但是确保文档只有一个标签 63 | 64 | 这将匹配只有一个 `search` 标签的文档,而不是匹配所有包含了 `search` 标签的文档。 65 | -------------------------------------------------------------------------------- /080_Structured_Search/40_bitsets.md: -------------------------------------------------------------------------------- 1 | ### 关于缓存 2 | 3 | 在【内部过滤操作】章节中,我们简单提到过过滤器是怎么计算的。它们的核心是一个字节集来表示哪些文档符合这个过滤器。Elasticsearch 主动缓存了这些字节集留作以后使用。一旦缓存后,当遇到相同的过滤时,这些字节集就可以被重用,而不需要重新运算整个过滤。 4 | 5 | 缓存的字节集很“聪明”:他们会增量更新。你索引中添加了新的文档,只有这些新文档需要被添加到已存的字节集中,而不是一遍遍重新计算整个缓存的过滤器。过滤器和整个系统的其他部分一样是实时的,你不需要关心缓存的过期时间。 6 | 7 | #### 独立的过滤缓存 8 | 9 | 每个过滤器都被独立计算和缓存,而不管它们在哪里使用。如果两个不同的查询使用相同的过滤器,则会使用相同的字节集。同样,如果一个查询在多处使用同样的过滤器,只有一个字节集会被计算和重用。 10 | 11 | 让我们看一下示例,查找符合下列条件的邮箱: 12 | 13 | * 在收件箱而且没有被读取过 14 | * _不在_收件箱但是被标记为重要 15 | 16 | ```json 17 | "bool": { 18 | "should": [ 19 | { "bool": { 20 | "must": [ 21 | { "term": { "folder": "inbox" }}, <1> 22 | { "term": { "read": false }} 23 | ] 24 | }}, 25 | { "bool": { 26 | "must_not": { 27 | "term": { "folder": "inbox" } <1> 28 | }, 29 | "must": { 30 | "term": { "important": true } 31 | } 32 | }} 33 | ] 34 | } 35 | ``` 36 | 37 | <1> 这两个过滤器相同,而且会使用同一个字节集。 38 | 39 | 虽然一个收件箱条件是 `must` 而另一个是 `must_not`,这两个条件本身是相等的。这意味着字节集会在第一个条件执行时计算一次,然后作为缓存被另一个条件使用。而第二次执行这条查询时,收件箱的过滤已经被缓存了,所以两个条件都能使用缓存的字节集。 40 | 41 | 这与查询 DSL 的组合型紧密相关。移动过滤器或在相同查询中多处重用相同的过滤器非常简单。这不仅仅是方便了开发者 —— 对于性能也有很大的提升 42 | 43 | #### 控制缓存 44 | 45 | 大部分直接处理字段的_枝叶过滤器_(例如 `term`)会被缓存,而像 `bool` 这类的组合过滤器则不会被缓存。 46 | 47 | 【提示】 48 | 49 | 枝叶过滤器需要在硬盘中检索倒排索引,所以缓存它们是有意义的。另一方面来说,组合过滤器使用快捷的字节逻辑来组合它们内部条件生成的字节集结果,所以每次重新计算它们也是很高效的。 50 | 51 | 然而,有部分枝叶过滤器,默认不会被缓存,因为它们这样做没有意义: 52 | 53 | 脚本过滤器: 54 | 55 | 脚本过滤器的结果不能被缓存因为脚本的意义对于 Elasticsearch 来说是不透明的。 56 | 57 | Geo 过滤器: 58 | 59 | 定位过滤器(我们会在【geoloc】中更详细的介绍),通常被用于过滤基于特定用户地理位置的结果。因为每个用户都有一个唯一的定位,geo 过滤器看起来不太会重用,所以缓存它们没有意义。 60 | 61 | 日期范围: 62 | 63 | 使用 `now` 方法的日期范围(例如 `"now-1h"`),结果值精确到毫秒。每次这个过滤器执行时,`now` 返回一个新的值。老的过滤器将不再被使用,所以默认缓存是被禁用的。然而,当 `now` 被取整时(例如,`now/d` 取最近一天),缓存默认是被启用的。 64 | 65 | 有时候默认的缓存测试并不正确。可能你希望一个复杂的 `bool` 表达式可以在相同的查询中重复使用,或你想要禁用一个 `date` 字段的过滤器缓存。你可以通过 `_cache` 标记来覆盖几乎所有过滤器的默认缓存策略 66 | 67 | ```json 68 | { 69 | "range" : { 70 | "timestamp" : { 71 | "gt" : "2014-01-02 16:15:14" <1> 72 | }, 73 | "_cache": false <2> 74 | } 75 | } 76 | ``` 77 | 78 | <1> 看起来我们不会再使用这个精确时间戳 79 | <2> 在这个过滤器上禁用缓存 80 | 81 | 以后的章节将提供一些例子来说明哪些时候覆盖默认缓存策略是有意义的。 82 | -------------------------------------------------------------------------------- /080_Structured_Search/45_filter_order.md: -------------------------------------------------------------------------------- 1 | ### 过滤顺序 2 | 3 | 在 `bool` 条件中过滤器的顺序对性能有很大的影响。更详细的过滤条件应该被放置在其他过滤器之前,以便在更早的排除更多的文档。 4 | 5 | 假如条件 A 匹配 1000 万个文档,而 B 只匹配 100 个文档,那么需要将 B 放在 A 前面。 6 | 7 | 缓存的过滤器非常快,所以它们需要被放在不能缓存的过滤器之前。想象一下我们有一个索引包含了一个月的日志事件,然而,我们只对近一个小时的事件感兴趣: 8 | 9 | ```json 10 | GET /logs/2014-01/_search 11 | { 12 | "query" : { 13 | "filtered" : { 14 | "filter" : { 15 | "range" : { 16 | "timestamp" : { 17 | "gt" : "now-1h" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | } 24 | ``` 25 | 26 | 这个过滤条件没有被缓存,因为它使用了 `now` 方法,这个值每毫秒都在变化。这意味着我们需要每次执行这条查询时都检测一整个月的日志事件。 27 | 28 | 我们可以通过组合一个缓存的过滤器来让这变得更有效率:我们可以添加一个含固定时间的过滤器来排除掉这个月的大部分数据,例如昨晚凌晨: 29 | 30 | ```json 31 | "bool": { 32 | "must": [ 33 | { "range" : { 34 | "timestamp" : { 35 | "gt" : "now-1h/d" <1> 36 | } 37 | }}, 38 | { "range" : { 39 | "timestamp" : { 40 | "gt" : "now-1h" <2> 41 | } 42 | }} 43 | ] 44 | } 45 | ``` 46 | 47 | <1> 这个过滤器被缓存了,因为它使用了取整到昨夜凌晨 `now` 条件。 48 | <2> 这个过滤器没有被缓存,因为它没有对 `now` 取整。 49 | 50 | `now-1h/d` 条件取整到昨夜凌晨,所以所有今天之前的文档都被排除掉了。这个结果的字节集被缓存了,因为 `now` 被取整了,意味着它只需要每天当_昨夜凌晨_的值改变时被执行一次。`now-1h` 条件没有被缓存,因为 `now` 表示最近一毫秒的时间。然而,得益于第一个过滤器,第二个过滤器只需要检测当天的文档就行。 51 | 52 | 这些条件的排序很重要。上面的实现能正常工作是因为_自从昨晚凌晨_条件比_最近一小时_条件位置更前。假如它们用别的方式组合,那么_最近一小时_条件还是需要检测所有的文档,而不仅仅是昨夜以来的文档。 53 | -------------------------------------------------------------------------------- /100_Full_Text_Search/00_Intro.md: -------------------------------------------------------------------------------- 1 | ##全文检索 2 | 3 | 我们已经介绍了简单的结构化查询,下面开始介绍_全文检索_:怎样对全文字段(full-text fields)进行检索以找到相关度最高的文档。 4 | 5 | 全文检索最重要的两个方面是: 6 | 7 | * 相关度(Relevance) 8 | 9 | 根据文档与查询的相关程度对结果集进行排序的能力。相关度可以使用TF/IDF、地理位置相近程度、模糊相似度或其他算法计算。 10 | 11 | * 分析(Analysis) 12 | 13 | 将一段文本转换为一组唯一的、标准化了的标记(token),用以(a)创建倒排索引,(b)查询倒排索引。 14 | 15 | 注意,一旦提到相关度和分析,指的都是查询(queries)而非过滤器(filters)。 16 | 17 | ### 基于短语 vs. 全文 18 | 19 | 虽然所有的查询都会进行相关度计算,但不是所有的查询都有分析阶段。而且像``bool``或``function_score``这样的查询并不在文本字段执行。文本查询可以分为两大类: 20 | 21 | 22 | #### 1. 基于短语(Term-based)的查询: 23 | 24 | 像``term``或``fuzzy``一类的查询是低级查询,它们没有分析阶段。这些查询在单一的短语上执行。例如对单词``'Foo'``的``term``查询会在倒排索引里__精确地__查找``'Foo'``这个词,并对每个包含这个单词的文档计算TF/IDF相关度``'_score'``。 25 | 26 | 牢记``term``查询只在倒排查询里精确地查找特定短语,而不会匹配短语的其它变形,如``foo``或``FOO``。不管短语怎样被加入索引,都只匹配倒排索引里的准确值。如果你在一个设置了``'not_analyzed'``的字段为``'["Foo", "Bar"]'``建索引,或者在一个用``'whitespace'``解析器解析的字段为``'Foo Bar'``建索引,都会在倒排索引里加入两个索引``'Foo'``和``'Bar'``。 27 | 28 | 29 | #### 2. 全文(Full-text)检索 30 | 31 | ``match``和``query_string``这样的查询是高级查询,它们会对字段进行分析: 32 | 33 | * 如果检索一个``'date'``或``'integer'``字段,它们会把查询语句作为日期或者整数格式数据。 34 | 35 | * 如果检索一个准确值(``'not_analyzed'``)字符串字段,它们会把整个查询语句作为一个短语。 36 | 37 | * 如果检索一个全文(``'analyzed'``)字段,查询会先用适当的解析器解析查询语句,产生需要查询的短语列表。然后对列表中的每个短语执行低级查询,合并查询结果,得到最终的文档相关度。 38 | 我们将会在后续章节讨论这一过程的细节。 39 | 40 | 我们很少需要直接使用基于短语的查询。通常我们会想要检索全文,而不是单独的短语,使用高级的全文检索会更简单(全文检索内部最终还是使用基于短语的查询)。 41 | 42 | 43 | ####[提示] 44 | 45 | 如果确实要查询一个准确值字段(``'not_analyzed'``),需要考虑使用查询还是过滤器。 46 | 47 | 单一短语的查询通常相当于__是/否__问题,用过滤器可以更好的描述这类查询,并且过滤器缓存可以提升性能: 48 | 49 | GET /_search 50 | { 51 | "query": { 52 | "filtered": { 53 | "filter": { 54 | "term": { "gender": "female" } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /100_Full_Text_Search/05_Match_query.md: -------------------------------------------------------------------------------- 1 | ### 匹配查询 2 | 3 | 不管你搜索什么内容,`match`查询是你首先需要接触的查询。它是一个高级查询,意味着`match`查询知道如何更好的处理全文检索和准确值检索。 4 | 5 | 这也就是说,`match`查询的一个主要用途是进行全文搜索。让我们通过一个小例子来看一下全文搜索是如何工作的。 6 | 7 | #### 索引一些数据 8 | 9 | 首先,我们使用`bulk` API来创建和索引一些文档: 10 | 11 | ```json 12 | DELETE /my_index <1> 13 | 14 | PUT /my_index 15 | { "settings": { "number_of_shards": 1 }} <2> 16 | 17 | POST /my_index/my_type/_bulk 18 | { "index": { "_id": 1 }} 19 | { "title": "The quick brown fox" } 20 | { "index": { "_id": 2 }} 21 | { "title": "The quick brown fox jumps over the lazy dog" } 22 | { "index": { "_id": 3 }} 23 | { "title": "The quick brown fox jumps over the quick dog" } 24 | { "index": { "_id": 4 }} 25 | { "title": "Brown fox brown dog" } 26 | ``` 27 | 28 | // SENSE: 100_Full_Text_Search/05_Match_query.json 29 | 30 | <1> 删除已经存在的索引(如果索引存在) 31 | <2> 然后,`关联失效`这一节解释了为什么我们创建该索引的时候只使用一个主分片。 32 | 33 | #### 单词查询 34 | 35 | 第一个例子解释了当使用`match`查询进行单词全文搜索时发生了什么: 36 | 37 | ```json 38 | GET /my_index/my_type/_search 39 | { 40 | "query": { 41 | "match": { 42 | "title": "QUICK!" 43 | } 44 | } 45 | } 46 | ``` 47 | // SENSE: 100_Full_Text_Search/05_Match_query.json 48 | 49 | Elasticsearch通过下面的步骤执行`match`查询: 50 | 51 | 1. _检查field类型_ 52 | `title`字段是一个字符串(`analyzed`),所以该查询字符串也需要被分析(`analyzed`) 53 | 54 | 2. _分析查询字符串_ 55 | 查询词`QUICK!`经过标准分析器的分析后变成单词`quick`。因为我们只有一个查询词,因此`match`查询可以以一种低级别`term`查询的方式执行。 56 | 57 | 3. _找到匹配的文档_ 58 | `term`查询在倒排索引中搜索`quick`,并且返回包含该词的文档。在这个例子中,返回的文档是1,2,3。 59 | 60 | 4. _为每个文档打分_ 61 | `term`查询综合考虑词频(每篇文档`title`字段包含`quick`的次数)、逆文档频率(在全部文档中`title`字段包含`quick`的次数)、包含`quick`的字段长度(长度越短越相关)来计算每篇文档的相关性得分`_score`。(更多请见相关性介绍) 62 | 63 | 这个过程之后我们将得到以下结果(简化后): 64 | 65 | ```json 66 | "hits": [ 67 | { 68 | "_id": "1", 69 | "_score": 0.5, <1> 70 | "_source": { 71 | "title": "The quick brown fox" 72 | } 73 | }, 74 | { 75 | "_id": "3", 76 | "_score": 0.44194174, <2> 77 | "_source": { 78 | "title": "The quick brown fox jumps over the quick dog" 79 | } 80 | }, 81 | { 82 | "_id": "2", 83 | "_score": 0.3125, <2> 84 | "_source": { 85 | "title": "The quick brown fox jumps over the lazy dog" 86 | } 87 | } 88 | ] 89 | ``` 90 | 91 | <1> 文档1最相关,因为 `title` 最短,意味着`quick`在语义中起比较大的作用。 92 | <2> 文档3比文档2更相关,因为在文档3中`quick`出现了两次。 93 | 94 | -------------------------------------------------------------------------------- /110_Multi_Field_Search/00_Intro.md: -------------------------------------------------------------------------------- 1 | 2 | ## 多字段搜索 3 | 4 | 只有一个简单的`match`子句的查询是很少的。我们经常需要在一个或者多个字段中查询相同的或者不同的查询字符串,意味着我们需要能够组合多个查询子句以及使他们的相关性得分有意义。 5 | 6 | 或许我们在寻找列夫·托尔斯泰写的一本叫《战争与和平》的书。或许我们在Elasticsearch的文档中查找`minimum should match`,它可能在标题中,或者在一页的正文中。或许我们查找名为John,姓为Smith的人。 7 | 8 | 在这一章节,我们会介绍用于构建多个查询子句搜索的可能的工具,以及怎么样选择解决方案来应用到你特殊的场景。 9 | 10 | 29 | -------------------------------------------------------------------------------- /120_Proximity_Matching/20_Scoring.md: -------------------------------------------------------------------------------- 1 | === Closer Is Better 2 | 3 | Whereas a phrase query simply excludes documents that don't contain the exact 4 | query phrase, a _proximity query_—a ((("proximity matching", "proximity queries")))((("slop parameter", "proximity queries and")))phrase query where `slop` is greater 5 | than `0`—incorporates the proximity of the query terms into the final 6 | relevance `_score`. By setting a high `slop` value like `50` or `100`, you can 7 | exclude documents in which the words are really too far apart, but give a higher 8 | score to documents in which the words are closer together. 9 | 10 | The following proximity query for `quick dog` matches both documents that 11 | contain the words `quick` and `dog`, but gives a higher score to the 12 | document((("relevance scores", "for proximity queries"))) in which the words are nearer to each other: 13 | 14 | [source,js] 15 | -------------------------------------------------- 16 | POST /my_index/my_type/_search 17 | { 18 | "query": { 19 | "match_phrase": { 20 | "title": { 21 | "query": "quick dog", 22 | "slop": 50 <1> 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------- 28 | // SENSE: 120_Proximity_Matching/20_Scoring.json 29 | 30 | <1> Note the high `slop` value. 31 | 32 | [source,js] 33 | -------------------------------------------------- 34 | { 35 | "hits": [ 36 | { 37 | "_id": "3", 38 | "_score": 0.75, <1> 39 | "_source": { 40 | "title": "The quick brown fox jumps over the quick dog" 41 | } 42 | }, 43 | { 44 | "_id": "2", 45 | "_score": 0.28347334, <2> 46 | "_source": { 47 | "title": "The quick brown fox jumps over the lazy dog" 48 | } 49 | } 50 | ] 51 | } 52 | -------------------------------------------------- 53 | <1> Higher score because `quick` and `dog` are close together 54 | <2> Lower score because `quick` and `dog` are further apart 55 | -------------------------------------------------------------------------------- /120_Proximity_Matching/25_Relevance.md: -------------------------------------------------------------------------------- 1 | [[proximity-relevance]] 2 | === Proximity for Relevance 3 | 4 | Although proximity queries are useful, the fact that they require all terms to be 5 | present can make them overly strict.((("proximity matching", "using for relevance")))((("relevance", "proximity queries for"))) It's the same issue that we discussed in 6 | <> in <>: if six out of seven terms match, 7 | a document is probably relevant enough to be worth showing to the user, but 8 | the `match_phrase` query would exclude it. 9 | 10 | Instead of using proximity matching as an absolute requirement, we can 11 | use it as a _signal_—as one of potentially many queries, each of which 12 | contributes to the overall score for each document (see <>). 13 | 14 | The fact that we want to add together the scores from multiple queries implies 15 | that we should combine them by using the `bool` query.((("bool query", "proximity query for relevance in"))) 16 | 17 | We can use a simple `match` query as a `must` clause. This is the query that 18 | will determine which documents are included in our result set. We can trim 19 | the long tail with the `minimum_should_match` parameter. Then we can add other, 20 | more specific queries as `should` clauses. Every one that matches will 21 | increase the relevance of the matching docs. 22 | 23 | [source,js] 24 | -------------------------------------------------- 25 | GET /my_index/my_type/_search 26 | { 27 | "query": { 28 | "bool": { 29 | "must": { 30 | "match": { <1> 31 | "title": { 32 | "query": "quick brown fox", 33 | "minimum_should_match": "30%" 34 | } 35 | } 36 | }, 37 | "should": { 38 | "match_phrase": { <2> 39 | "title": { 40 | "query": "quick brown fox", 41 | "slop": 50 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------- 49 | // SENSE: 120_Proximity_Matching/25_Relevance.json 50 | 51 | <1> The `must` clause includes or excludes documents from the result set. 52 | <2> The `should` clause increases the relevance score of those documents that 53 | match. 54 | 55 | We could, of course, include other queries in the `should` clause, where each 56 | query targets a specific aspect of relevance. 57 | -------------------------------------------------------------------------------- /130_Partial_Matching/00_Intro.md: -------------------------------------------------------------------------------- 1 | [[partial-matching]] 2 | == Partial Matching 3 | 4 | A keen observer will notice that all the queries so far in this book have 5 | operated on whole terms.((("partial matching"))) To match something, the smallest unit had to be a 6 | single term. You can find only terms that exist in the inverted index. 7 | 8 | But what happens if you want to match parts of a term but not the whole thing? 9 | _Partial matching_ allows users to specify a portion of the term they are 10 | looking for and find any words that contain that fragment. 11 | 12 | The requirement to match on part of a term is less common in the full-text 13 | search-engine world than you might think. If you have come from an SQL 14 | background, you likely have, at some stage of your career, 15 | implemented a _poor man's full-text search_ using SQL constructs like this: 16 | 17 | [source,js] 18 | -------------------------------------------------- 19 | WHERE text LIKE "*quick*" 20 | AND text LIKE "*brown*" 21 | AND text LIKE "*fox*" <1> 22 | -------------------------------------------------- 23 | 24 | <1> `*fox*` would match ``fox'' and ``foxes.'' 25 | 26 | Of course, with Elasticsearch, we have the analysis process and the inverted 27 | index that remove the need for such brute-force techniques. To handle the 28 | case of matching both ``fox'' and ``foxes,'' we could simply use a stemmer to 29 | index words in their root form. There is no need to match partial terms. 30 | 31 | That said, on some occasions partial matching can be useful. 32 | Common use ((("partial matching", "common use cases")))cases include the following: 33 | 34 | * Matching postal codes, product serial numbers, or other `not_analyzed` values 35 | that start with a particular prefix or match a wildcard pattern 36 | or even a regular expression 37 | 38 | * _search-as-you-type_—displaying the most likely results before the 39 | user has finished typing the search terms 40 | 41 | * Matching in languages like German or Dutch, which contain long compound 42 | words, like _Weltgesundheitsorganisation_ (World Health Organization) 43 | 44 | We will start by examining prefix matching on exact-value `not_analyzed` 45 | fields. 46 | -------------------------------------------------------------------------------- /130_Partial_Matching/05_Postcodes.md: -------------------------------------------------------------------------------- 1 | === Postcodes and Structured Data 2 | 3 | We will use United Kingdom postcodes (postal codes in the United States) to illustrate how((("partial matching", "postcodes and structured data"))) to use partial matching with 4 | structured data. UK postcodes have a well-defined structure. For instance, the 5 | postcode `W1V 3DG` can((("postcodes (UK), partial matching with"))) be broken down as follows: 6 | 7 | * `W1V`: This outer part identifies the postal area and district: 8 | 9 | ** `W` indicates the area (one or two letters) 10 | ** `1V` indicates the district (one or two numbers, possibly followed by a letter 11 | 12 | * `3DG`: This inner part identifies a street or building: 13 | 14 | ** `3` indicates the sector (one number) 15 | ** `DG` indicates the unit (two letters) 16 | 17 | 18 | Let's assume that we are indexing postcodes as exact-value `not_analyzed` 19 | fields, so we could create our index as follows: 20 | 21 | [source,js] 22 | -------------------------------------------------- 23 | PUT /my_index 24 | { 25 | "mappings": { 26 | "address": { 27 | "properties": { 28 | "postcode": { 29 | "type": "string", 30 | "index": "not_analyzed" 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------- 37 | // SENSE: 130_Partial_Matching/10_Prefix_query.json 38 | 39 | And index some ((("indexing", "postcodes")))postcodes: 40 | 41 | [source,js] 42 | -------------------------------------------------- 43 | PUT /my_index/address/1 44 | { "postcode": "W1V 3DG" } 45 | 46 | PUT /my_index/address/2 47 | { "postcode": "W2F 8HW" } 48 | 49 | PUT /my_index/address/3 50 | { "postcode": "W1F 7HW" } 51 | 52 | PUT /my_index/address/4 53 | { "postcode": "WC1N 1LZ" } 54 | 55 | PUT /my_index/address/5 56 | { "postcode": "SW5 0BE" } 57 | -------------------------------------------------- 58 | // SENSE: 130_Partial_Matching/10_Prefix_query.json 59 | 60 | Now our data is ready to be queried. 61 | -------------------------------------------------------------------------------- /130_Partial_Matching/25_Index_time.md: -------------------------------------------------------------------------------- 1 | === Index-Time Optimizations 2 | 3 | All of the solutions we've talked about so far are implemented at 4 | _query time_. ((("index time optimizations")))((("partial matching", "index time optimizations")))They don't require any special mappings or indexing patterns; 5 | they simply work with the data that you've already indexed. 6 | 7 | The flexibility of query-time operations comes at a cost: search performance. 8 | Sometimes it may make sense to move the cost away from the query. In a real- 9 | time web application, an additional 100ms may be too much latency to tolerate. 10 | 11 | By preparing your data at index time, you can make your searches more flexible 12 | and improve performance. You still pay a price: increased index size and 13 | slightly slower indexing throughput, but it is a price you pay once at index 14 | time, instead of paying it on every query. 15 | 16 | Your users will thank you. 17 | -------------------------------------------------------------------------------- /130_Partial_Matching/30_Ngram_intro.md: -------------------------------------------------------------------------------- 1 | === Ngrams for Partial Matching 2 | 3 | As we have said before, ``You can find only terms that exist in the inverted 4 | index.'' Although the `prefix`, `wildcard`, and `regexp` queries demonstrated that 5 | that is not strictly true, it _is_ true that doing a single-term lookup is 6 | much faster than iterating through the terms list to find matching terms on 7 | the fly.((("partial matching", "index time optimizations", "n-grams"))) Preparing your data for partial matching ahead of time will increase 8 | your search performance. 9 | 10 | Preparing your data at index time means choosing the right analysis chain, and 11 | the tool that we use for partial matching is the _n-gram_.((("n-grams"))) An n-gram can be 12 | best thought of as a _moving window on a word_. The _n_ stands for a length. 13 | If we were to n-gram the word `quick`, the results would depend on the length 14 | we have chosen: 15 | 16 | [horizontal] 17 | * Length 1 (unigram): [ `q`, `u`, `i`, `c`, `k` ] 18 | * Length 2 (bigram): [ `qu`, `ui`, `ic`, `ck` ] 19 | * Length 3 (trigram): [ `qui`, `uic`, `ick` ] 20 | * Length 4 (four-gram): [ `quic`, `uick` ] 21 | * Length 5 (five-gram): [ `quick` ] 22 | 23 | Plain n-grams are useful for matching _somewhere within a word_, a technique 24 | that we will use in <>. However, for search-as-you-type, 25 | we use a specialized form of n-grams called _edge n-grams_. ((("edge n-grams"))) Edge 26 | n-grams are anchored to the beginning of the word. Edge n-gramming the word 27 | `quick` would result in this: 28 | 29 | * `q` 30 | * `qu` 31 | * `qui` 32 | * `quic` 33 | * `quick` 34 | 35 | You may notice that this conforms exactly to the letters that a user searching for ``quick'' would type. In other words, these are the 36 | perfect terms to use for instant search! 37 | -------------------------------------------------------------------------------- /170_Relevance/05_Intro.md: -------------------------------------------------------------------------------- 1 | [[controlling-relevance]] 2 | == Controlling Relevance 3 | 4 | Databases that deal purely in structured data (such as dates, numbers, and 5 | string enums) have it easy: they((("relevance", "controlling"))) just have to check whether a document (or a 6 | row, in a relational database) matches the query. 7 | 8 | While Boolean yes/no matches are an essential part of full-text search, they 9 | are not enough by themselves. Instead, we also need to know how relevant each 10 | document is to the query. Full-text search engines have to not only find the 11 | matching documents, but also sort them by relevance. 12 | 13 | Full-text relevance ((("similarity algorithms")))formulae, or _similarity algorithms_, combine several 14 | factors to produce a single relevance `_score` for each document. In this 15 | chapter, we examine the various moving parts and discuss how they can be 16 | controlled. 17 | 18 | Of course, relevance is not just about full-text queries; it may need to 19 | take structured data into account as well. Perhaps we are looking for a 20 | vacation home with particular features (air-conditioning, sea view, free 21 | WiFi). The more features that a property has, the more relevant it is. Or 22 | perhaps we want to factor in sliding scales like recency, price, popularity, or 23 | distance, while still taking the relevance of a full-text query into account. 24 | 25 | All of this is possible thanks to the powerful scoring infrastructure 26 | available in Elasticsearch. 27 | 28 | We will start by looking at the theoretical side of how Lucene calculates 29 | relevance, and then move on to practical examples of how you can control the 30 | process. 31 | -------------------------------------------------------------------------------- /170_Relevance/30_Not_quite_not.md: -------------------------------------------------------------------------------- 1 | [[not-quite-not]] 2 | === Not Quite Not 3 | 4 | A search on the Internet for ``Apple'' is likely to return results about the 5 | company, the fruit, ((("relevance", "controlling", "must_not clause in bool query")))((("bool query", "must_not clause")))and various recipes. We could try to narrow it down to 6 | just the company by excluding words like `pie`, `tart`, `crumble`, and `tree`, 7 | using a `must_not` clause in a `bool` query: 8 | 9 | [source,json] 10 | ------------------------------- 11 | GET /_search 12 | { 13 | "query": { 14 | "bool": { 15 | "must": { 16 | "match": { 17 | "text": "apple" 18 | } 19 | }, 20 | "must_not": { 21 | "match": { 22 | "text": "pie tart fruit crumble tree" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | ------------------------------- 29 | 30 | But who is to say that we wouldn't miss a very relevant document about Apple 31 | the company by excluding `tree` or `crumble`? Sometimes, `must_not` can be 32 | too strict. 33 | 34 | [[boosting-query]] 35 | ==== boosting Query 36 | 37 | The http://bit.ly/1IO281f[`boosting` query] solves((("boosting query")))((("relevance", "controlling", "boosting query"))) this problem. 38 | It allows us to still include results that appear to be about the fruit or 39 | the pastries, but to downgrade them--to rank them lower than they would 40 | otherwise be: 41 | 42 | [source,json] 43 | ------------------------------- 44 | GET /_search 45 | { 46 | "query": { 47 | "boosting": { 48 | "positive": { 49 | "match": { 50 | "text": "apple" 51 | } 52 | }, 53 | "negative": { 54 | "match": { 55 | "text": "pie tart fruit crumble tree" 56 | } 57 | }, 58 | "negative_boost": 0.5 59 | } 60 | } 61 | } 62 | ------------------------------- 63 | 64 | It accepts a `positive` query and a `negative` query.((("positive query and negative query (in boosting query)"))) Only documents that 65 | match the `positive` query will be included in the results list, but documents 66 | that also match the `negative` query will be downgraded by multiplying the 67 | original `_score` of((("negative_boost"))) the document with the `negative_boost`. 68 | 69 | For this to work, the `negative_boost` must be less than `1.0`. In this 70 | example, any documents that contain any of the negative terms will have their 71 | `_score` cut in half. 72 | -------------------------------------------------------------------------------- /170_Relevance/40_Function_score_query.md: -------------------------------------------------------------------------------- 1 | [[function-score-query]] 2 | === function_score Query 3 | 4 | The http://bit.ly/1sCKtHW[`function_score` query] is the 5 | ultimate tool for taking control of the scoring process.((("function_score query")))((("relevance", "controlling", "function_score query"))) It allows you to 6 | apply a function to each document that matches the main query in order to 7 | alter or completely replace the original query `_score`. 8 | 9 | In fact, you can apply different functions to _subsets_ of the main result set by 10 | using filters, which gives you the best of both worlds: efficient scoring with 11 | cacheable filters. 12 | 13 | It supports several predefined functions out of the box: 14 | 15 | `weight`:: 16 | 17 | Apply a simple boost to each document without the boost being 18 | normalized: a `weight` of `2` results in `2 * _score`. 19 | 20 | `field_value_factor`:: 21 | 22 | Use the value of a field in the document to alter the `_score`, such as 23 | factoring in a `popularity` count or number of `votes`. 24 | 25 | `random_score`:: 26 | 27 | Use consistently random scoring to sort results differently for every user, 28 | while maintaining the same sort order for a single user. 29 | 30 | _Decay functions_—`linear`, `exp`, `gauss`:: 31 | 32 | Incorporate sliding-scale values like `publish_date`, `geo_location`, or 33 | `price` into the `_score` to prefer recently published documents, documents 34 | near a latitude/longitude (lat/lon) point, or documents near a specified price point. 35 | 36 | `script_score`:: 37 | 38 | Use a custom script to take complete control of the scoring logic. If your 39 | needs extend beyond those of the functions in this list, write a custom 40 | script to implement the logic that you need. 41 | 42 | Without the `function_score` query, we would not be able to combine the score 43 | from a full-text query with a factor like recency. We would have to sort 44 | either by `_score` or by `date`; the effect of one would obliterate the 45 | effect of the other. This query allows you to blend the two together: to still 46 | sort by full-text relevance, but giving extra weight to recently published 47 | documents, or popular documents, or products that are near the user's price 48 | point. As you can imagine, a query that supports all of this can look fairly 49 | complex. We'll start with a simple use case and work our way up the 50 | complexity ladder. 51 | -------------------------------------------------------------------------------- /170_Relevance/80_Conclusion.md: -------------------------------------------------------------------------------- 1 | 2 | [[relevance-conclusion]] 3 | === Relevance Tuning Is the Last 10% 4 | 5 | In this chapter, we looked at a how Lucene generates scores based on TF/IDF. 6 | Understanding the score-generation process((("relevance", "controlling", "tuning relevance"))) is critical so you can 7 | tune, modulate, attenuate, and manipulate the score for your particular 8 | business domain. 9 | 10 | In practice, simple combinations of queries will get you good search results. 11 | But to get _great_ search results, you'll often have to start tinkering with 12 | the previously mentioned tuning methods. 13 | 14 | Often, applying a boost on a strategic field or rearranging a query to 15 | emphasize a particular clause will be sufficient to make your results great. 16 | Sometimes you'll need more-invasive changes. This is usually the case if your 17 | scoring requirements diverge heavily from Lucene's word-based TF/IDF model (for example, you 18 | want to score based on time or distance). 19 | 20 | With that said, relevancy tuning is a rabbit hole that you can easily fall into 21 | and never emerge. The concept of _most relevant_ is a nebulous target to hit, and 22 | different people often have different ideas about document ranking. It is easy 23 | to get into a cycle of constant fiddling without any apparent progress. 24 | 25 | We encourage you to avoid this (very tempting) behavior and instead properly 26 | instrument your search results. Monitor how often your users click the top 27 | result, the top 10, and the first page; how often they execute a secondary query 28 | without selecting a result first; how often they click a result and immediately 29 | go back to the search results, and so forth. 30 | 31 | These are all indicators of how relevant your search results are to the user. 32 | If your query is returning highly relevant results, users will select one of 33 | the top-five results, find what they want, and leave. Irrelevant results cause 34 | users to click around and try new search queries. 35 | 36 | Once you have instrumentation in place, tuning your query is simple. Make a change, 37 | monitor its effect on your users, and repeat as necessary. The tools outlined in this 38 | chapter are just that: tools. You have to use them appropriately to propel 39 | your search results into the _great_ category, and the only way to do that is with 40 | strong measurement of user behavior. 41 | -------------------------------------------------------------------------------- /200_Language_intro/00_Intro.md: -------------------------------------------------------------------------------- 1 | [[language-intro]] 2 | == Getting Started with Languages 3 | 4 | Elasticsearch ships with a collection of language analyzers that provide 5 | good, basic, out-of-the-box ((("language analyzers")))((("languages", "getting started with")))support for many of the world's most common 6 | languages: 7 | 8 | Arabic, Armenian, Basque, Brazilian, Bulgarian, Catalan, Chinese, 9 | Czech, Danish, Dutch, English, Finnish, French, Galician, German, Greek, 10 | Hindi, Hungarian, Indonesian, Irish, Italian, Japanese, Korean, Kurdish, 11 | Norwegian, Persian, Portuguese, Romanian, Russian, Spanish, Swedish, 12 | Turkish, and Thai. 13 | 14 | These analyzers typically((("language analyzers", "roles performed by"))) perform four roles: 15 | 16 | * Tokenize text into individual words: 17 | + 18 | `The quick brown foxes` -> [`The`, `quick`, `brown`, `foxes`] 19 | 20 | * Lowercase tokens: 21 | + 22 | `The` -> `the` 23 | 24 | * Remove common _stopwords_: 25 | + 26 | [`The`, `quick`, `brown`, `foxes`] -> [`quick`, `brown`, `foxes`] 27 | 28 | * Stem tokens to their root form: 29 | + 30 | `foxes` -> `fox` 31 | 32 | Each analyzer may also apply other transformations specific to its language in 33 | order to make words from that((("language analyzers", "other transformations specific to the language"))) language more searchable: 34 | 35 | * The `english` analyzer ((("english analyzer")))removes the possessive `'s`: 36 | + 37 | `John's` -> `john` 38 | 39 | * The `french` analyzer ((("french analyzer")))removes _elisions_ like `l'` and `qu'` and 40 | _diacritics_ like `¨` or `^`: 41 | + 42 | `l'église` -> `eglis` 43 | 44 | * The `german` analyzer normalizes((("german analyzer"))) terms, replacing `ä` and `ae` with `a`, or 45 | `ß` with `ss`, among others: 46 | + 47 | `äußerst` -> `ausserst` 48 | 49 | -------------------------------------------------------------------------------- /200_Language_intro/70_Conclusion.md: -------------------------------------------------------------------------------- 1 | === Conclusion 2 | 3 | This chapter has explained how to get started with language analysis using the 4 | tools that are available to you out of the box. You may never need more -- it 5 | depends on the languages that you have to deal with and your business 6 | requirements. Getting language search right is a complex job which often 7 | requires custom configuration. The next chapters delve more deeply into the 8 | building blocks provided by Elasticsearch that you can join together to build 9 | your optimal solution. 10 | -------------------------------------------------------------------------------- /210_Identifying_words/00_Intro.md: -------------------------------------------------------------------------------- 1 | [[identifying-words]] 2 | == Identifying Words 3 | 4 | A word in English is relatively simple to spot: words are separated by 5 | whitespace or (some) punctuation.((("languages", "identifyig words")))((("words", "identifying"))) Even in English, though, there can be 6 | controversy: is _you're_ one word or two? What about _o'clock_, 7 | _cooperate_, _half-baked_, or _eyewitness_? 8 | 9 | Languages like German or Dutch combine individual words to create longer 10 | compound words like _Weißkopfseeadler_ (white-headed sea eagle), but in order 11 | to be able to return `Weißkopfseeadler` as a result for the query `Adler` 12 | (eagle), we need to understand how to break up compound words into their 13 | constituent parts. 14 | 15 | Asian languages are even more complex: some have no whitespace between words, 16 | sentences, or even paragraphs.((("Asian languages", "identifying words"))) Some words can be represented by a single 17 | character, but the same single character, when placed next to other 18 | characters, can form just one part of a longer word with a quite different 19 | meaning. 20 | 21 | It should be obvious that there is no silver-bullet analyzer that will 22 | miraculously deal with all human languages. Elasticsearch ships with dedicated 23 | analyzers for many languages, and more language-specific analyzers are 24 | available as plug-ins. 25 | 26 | However, not all languages have dedicated analyzers, and sometimes you won't 27 | even be sure which language(s) you are dealing with. For these situations, we 28 | need good standard tools that do a reasonable job regardless of language. 29 | -------------------------------------------------------------------------------- /210_Identifying_words/10_Standard_analyzer.md: -------------------------------------------------------------------------------- 1 | [[standard-analyzer]] 2 | === standard Analyzer 3 | 4 | The `standard` analyzer is used by default for any full-text `analyzed` string 5 | field. ((("standard analyzer"))) If we were to reimplement the `standard` analyzer as a 6 | <>, it would be defined as follows: 7 | 8 | [role="pagebreak-before"] 9 | [source,js] 10 | -------------------------------------------------- 11 | { 12 | "type": "custom", 13 | "tokenizer": "standard", 14 | "filter": [ "lowercase", "stop" ] 15 | } 16 | -------------------------------------------------- 17 | 18 | In <> and <>, we talk about the 19 | `lowercase`, and `stop` _token filters_, but for the moment, let's focus on 20 | the `standard` _tokenizer_. 21 | 22 | -------------------------------------------------------------------------------- /210_Identifying_words/30_ICU_plugin.md: -------------------------------------------------------------------------------- 1 | [[icu-plugin]] 2 | === Installing the ICU Plug-in 3 | 4 | The https://github.com/elasticsearch/elasticsearch-analysis-icu[ICU analysis 5 | plug-in] for Elasticsearch uses the _International Components for Unicode_ 6 | (ICU) libraries (see http://site.icu-project.org[site.project.org]) to 7 | provide a rich set of tools for dealing with Unicode.((("International Components for Unicode libraries", see="ICU plugin, installing")))((("words", "identifying", "installing ICU plugin")))((("ICU plugin, installing"))) These include the 8 | `icu_tokenizer`, which is particularly useful for Asian languages,((("Asian languages", "icu_tokenizer for"))) and a number 9 | of token filters that are essential for correct matching and sorting in all 10 | languages other than English. 11 | 12 | [NOTE] 13 | ================================================== 14 | 15 | The ICU plug-in is an essential tool for dealing with languages other than 16 | English, and it is highly recommended that you install and use it. 17 | Unfortunately, because it is based on the external ICU libraries, different 18 | versions of the ICU plug-in may not be compatible with previous versions. When 19 | upgrading, you may need to reindex your data. 20 | 21 | ================================================== 22 | 23 | To install the plug-in, first shut down your Elasticsearch node and then run the 24 | following command from the Elasticsearch home directory: 25 | 26 | [source,sh] 27 | -------------------------------------------------- 28 | ./bin/plugin -install elasticsearch/elasticsearch-analysis-icu/$VERSION <1> 29 | -------------------------------------------------- 30 | 31 | <1> The current `$VERSION` can be found at 32 | _https://github.com/elasticsearch/elasticsearch-analysis-icu_. 33 | 34 | Once installed, restart Elasticsearch, and you should see a line similar to the 35 | following in the startup logs: 36 | 37 | [INFO][plugins] [Mysterio] loaded [marvel, analysis-icu], sites [marvel] 38 | 39 | If you are running a cluster with multiple nodes, you will need to install the 40 | plug-in on every node in the cluster. 41 | -------------------------------------------------------------------------------- /220_Token_normalization/00_Intro.md: -------------------------------------------------------------------------------- 1 | [[token-normalization]] 2 | == Normalizing Tokens 3 | 4 | Breaking text into tokens is ((("normalization", "of tokens")))((("tokens", "normalizing")))only half the job. To make those 5 | tokens more easily searchable, they need to go through a _normalization_ 6 | process to remove insignificant differences between otherwise identical words, 7 | such as uppercase versus lowercase. Perhaps we also need to remove significant 8 | differences, to make `esta`, `ésta`, and `está` all searchable as the same 9 | word. Would you search for `déjà vu`, or just for `deja vu`? 10 | 11 | This is the job of the token filters, which((("token filters"))) receive a stream of tokens from 12 | the tokenizer. You can have multiple token filters, each doing its particular 13 | job. Each receives the new token stream as output by the token filter before 14 | it. 15 | 16 | -------------------------------------------------------------------------------- /220_Token_normalization/10_Lowercasing.md: -------------------------------------------------------------------------------- 1 | [[lowercase-token-filter]] 2 | === In That Case 3 | 4 | The most frequently used token filter is the `lowercase` filter, which does 5 | exactly what you would expect; it transforms ((("tokens", "normalizing", "lowercase filter")))((("lowercase token filter")))each token into its lowercase 6 | form: 7 | 8 | [source,js] 9 | -------------------------------------------------- 10 | GET /_analyze?tokenizer=standard&filters=lowercase 11 | The QUICK Brown FOX! <1> 12 | -------------------------------------------------- 13 | <1> Emits tokens `the`, `quick`, `brown`, `fox` 14 | 15 | It doesn't matter whether users search for `fox` or `FOX`, as long as the same 16 | analysis process is applied at query time and at search time. The `lowercase` 17 | filter will transform a query for `FOX` into a query for `fox`, which is the 18 | same token that we have stored in our inverted index. 19 | 20 | To use token filters as part of the analysis process, we ((("analyzers", "using token filters")))((("token filters", "using with analyzers")))can create a `custom` 21 | analyzer: 22 | 23 | [source,js] 24 | -------------------------------------------------- 25 | PUT /my_index 26 | { 27 | "settings": { 28 | "analysis": { 29 | "analyzer": { 30 | "my_lowercaser": { 31 | "tokenizer": "standard", 32 | "filter": [ "lowercase" ] 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------- 39 | 40 | And we can test it out with the `analyze` API: 41 | 42 | [source,js] 43 | -------------------------------------------------- 44 | GET /my_index/_analyze?analyzer=my_lowercaser 45 | The QUICK Brown FOX! <1> 46 | -------------------------------------------------- 47 | <1> Emits tokens `the`, `quick`, `brown`, `fox` 48 | 49 | -------------------------------------------------------------------------------- /240_Stopwords/70_Relevance.md: -------------------------------------------------------------------------------- 1 | [[stopwords-relavance]] 2 | === Stopwords and Relevance 3 | 4 | The last topic to cover before moving on from stopwords((("stopwords", "relevance and")))((("relevance", "stopwords and"))) is that of relevance. 5 | Leaving stopwords in your index could make the relevance calculation 6 | less accurate, especially if your documents are very long. 7 | 8 | As we have already discussed in <>, the((("BM25", "term frequency saturation"))) reason for this is 9 | that <> doesn't impose an 10 | upper limit on the impact of term frequency.((("Term Frequency/Inverse Document Frequency (TF/IDF) similarity algorithm", "stopwords and"))) Very common words may have a low 11 | weight because of inverse document frequency but, in long documents, the sheer 12 | number of occurrences of stopwords in a single document may lead to their 13 | weight being artificially boosted. 14 | 15 | You may want to consider using the <> similarity on long 16 | fields that include stopwords instead of the default Lucene similarity. 17 | 18 | -------------------------------------------------------------------------------- /260_Synonyms/10_Intro.md: -------------------------------------------------------------------------------- 1 | [[synonyms]] 2 | == Synonyms 3 | 4 | While stemming helps to broaden the scope of search by simplifying inflected 5 | words to their root form, synonyms((("synonyms"))) broaden the scope by relating concepts and 6 | ideas. Perhaps no documents match a query for ``English queen,'' but documents 7 | that contain ``British monarch'' would probably be considered a good match. 8 | 9 | A user might search for ``the US'' and expect to find documents that contain 10 | _United States_, _USA_, _U.S.A._, _America_, or _the States_. 11 | However, they wouldn't expect to see results about `the states of matter` or 12 | `state machines`. 13 | 14 | This example provides a valuable lesson. It demonstrates how simple it is for 15 | a human to distinguish between separate concepts, and how tricky it can be for 16 | mere machines. The natural tendency is to try to provide synonyms for every 17 | word in the language, to ensure that any document is findable with even the 18 | most remotely related terms. 19 | 20 | This is a mistake. In the same way that we prefer light or minimal stemming 21 | to aggressive stemming, synonyms should be used only where necessary. Users 22 | understand why their results are limited to the words in their search query. 23 | They are less understanding when their results seems almost random. 24 | 25 | Synonyms can be used to conflate words that have pretty much the same meaning, 26 | such as `jump`, `leap`, and `hop`, or `pamphlet`, `leaflet`, and `brochure`. 27 | Alternatively, they can be used to make a word more generic. For instance, 28 | `bird` could be used as a more general synonym for `owl` or `pigeon`, and `adult` 29 | could be used for `man` or `woman`. 30 | 31 | Synonyms appear to be a simple concept but they are quite tricky to get right. 32 | In this chapter, we explain the mechanics of using synonyms and discuss 33 | the limitations and gotchas. 34 | 35 | [TIP] 36 | ==== 37 | Synonyms are used to broaden the scope of what is considered a 38 | matching document. Just as with <> or 39 | <>, synonym fields should not be used 40 | alone but should be combined with a query on a main field that contains 41 | the original text in unadulterated form. See <> for an 42 | explanation of how to maintain relevance when using synonyms. 43 | ==== 44 | 45 | 46 | -------------------------------------------------------------------------------- /260_Synonyms/30_Synonym_formats.md: -------------------------------------------------------------------------------- 1 | [[synonym-formats]] 2 | === Formatting Synonyms 3 | 4 | In their simplest form, synonyms are((("synonyms", "formatting"))) listed as comma-separated values: 5 | 6 | "jump,leap,hop" 7 | 8 | If any of these terms is encountered, it is replaced by all of the listed 9 | synonyms. For instance: 10 | 11 | [role="pagebreak-before"] 12 | [source,text] 13 | -------------------------- 14 | Original terms: Replaced by: 15 | ──────────────────────────────── 16 | jump → (jump,leap,hop) 17 | leap → (jump,leap,hop) 18 | hop → (jump,leap,hop) 19 | -------------------------- 20 | 21 | Alternatively, with the `=>` syntax, it is possible to specify a list of terms 22 | to match (on the left side), and a list of one or more replacements (on 23 | the right side): 24 | 25 | "u s a,united states,united states of america => usa" 26 | "g b,gb,great britain => britain,england,scotland,wales" 27 | 28 | [source,text] 29 | -------------------------- 30 | Original terms: Replaced by: 31 | ──────────────────────────────── 32 | u s a → (usa) 33 | united states → (usa) 34 | great britain → (britain,england,scotland,wales) 35 | -------------------------- 36 | 37 | If multiple rules for the same synonyms are specified, they are merged 38 | together. The order of rules is not respected. Instead, the longest matching 39 | rule wins. Take the following rules as an example: 40 | 41 | "united states => usa", 42 | "united states of america => usa" 43 | 44 | If these rules conflicted, Elasticsearch would turn `United States of 45 | America` into the terms `(usa),(of),(america)`. Instead, the longest 46 | sequence wins, and we end up with just the term `(usa)`. 47 | 48 | -------------------------------------------------------------------------------- /270_Fuzzy_matching/10_Intro.md: -------------------------------------------------------------------------------- 1 | [[模糊-匹配]] 2 | == 打字错误 和 拼写错误 3 | 4 | 我们希望在结构化数据上的查询(如日期和价格)仅返回精确匹配的文档. 5 | ((("typoes and misspellings", "fuzzy matching")))((("fuzzy matching"))) 然而, 好的全文检索不应该有同样的限制. 相反, 我们能拓宽网络以包含那些 _可能的_匹配, 并且利用相关性分数把更好的匹配结果放在结果集的前面. 6 | 7 | 事实上, 仅能精确匹配的全文检索 ((("full text search", "fuzzy matching")))可能会让你的用户感到失望. 难道你不希望一个对 ``quick brown fox'' 的检索能匹配包含 8 | ``fast brown foxes,'' 的文档,对 ``Johnny Walker'' 的检索能匹配包含 9 | ``Johnnie Walker,'' 的文档 或 ``Arnold Shcwarzenneger'' 能匹配 ``Arnold 10 | Schwarzenegger''吗? 11 | 12 | 如果文档中存在 _确切_ 包含于用户查询的内容,它们应该出现在结果集的前面, 但更弱的匹配可能在下面的列表中. 13 | 如果没有精确匹配的文档, 至少我们应该为用户显示可能的匹配结果; 它们甚至有可能就是用户原来想要的! 14 | 15 | 我们已经在 <> 看过 diacritic-free 匹配, 16 | 在 <> 看过 词干提取, 在 <> 看过 同义词, 但是所有的这些方法都预先假定了单词是正确拼写的, 或者每个词只有一种拼写方式. 17 | 18 | 模糊匹配允许 查询-时 匹配拼写错误的单词, 音标表征过滤器能在索引时用于 _发音-相似_ 的匹配. 19 | 20 | -------------------------------------------------------------------------------- /270_Fuzzy_matching/20_Fuzziness.md: -------------------------------------------------------------------------------- 1 | [[模糊]] 2 | === 模糊 3 | 4 | _模糊匹配_ 视两个单词 ``模糊'' 相似,正好像它们是同一个词. 5 | ((("typoes and misspellings", "fuzziness, defining"))) 首先, 我们需要通过_fuzziness_ 来定义什么是((("fuzziness"))). 6 | 7 | 1965年, Vladimir Levenshtein 开发了 8 | http://en.wikipedia.org/wiki/Levenshtein_distance[Levenshtein distance(Levenshtein距离)], 用来度量把一个单词转换为另一个单词需要的单字符编辑次数 ((("Levenshtein distance"))). 9 | 他提出了3种单字符编辑: 10 | 11 | * _替换_ 一个字符到另一个字符: _f_ox -> _b_ox 12 | 13 | * _插入_ 一个新字符: sic -> sic_k_ 14 | 15 | * _删除_ 一个字符:: b_l_ack -> back 16 | 17 | http://en.wikipedia.org/wiki/Frederick_J._Damerau[Frederick Damerau] 18 | 稍后扩展了这些操作并包含了1个新的 ((("Damerau, Frederick J."))): 19 | 20 | * _换位_ 调整字符: _st_ar -> _ts_ar 21 | 22 | 例如,把 `bieber` 转换为 `beaver` 需要以下几步: 23 | 24 | 1. 用 `v` 替换掉 `b`: bie_b_er -> bie_v_er 25 | 2. 用 `a` 替换掉 `i`: b_i_ever -> b_a_ever 26 | 3. 换位 `a` 和 `e` : b_ae_ver -> b_ea_ver 27 | 28 | 以上的3步代表了3个 29 | http://bit.ly/1ymgZPB[Damerau-Levenshtein edit distance(Damerau-Levenshtein编辑距离)]. 30 | 31 | 显然, `bieber` 距 `beaver`—很远;远得无法被认为是一个简单的拼写错误. 32 | Damerau发现 80% 的人类拼写错误的编辑距离都是1. 换句话说, 80% 的拼写错误都可以通过 _单次编辑_ 33 | 修改为原始的字符串. 34 | 35 | 通过指定 `fuzziness` 参数为 2,Elasticsearch 支持最大的编辑距离. 36 | 37 | 当然, 一个字符串的单次编辑次数依赖于它的长度. 对 `hat` 进行两次编辑可以得到 `mad`, 38 | 所以允许对长度为3的字符串进行两次修改就太过了. `fuzziness` 39 | 参数可以被设置成 `AUTO`, 结果会在下面的最大编辑距离中: 40 | 41 | * `0` 1或2个字符的字符串 42 | * `1` 3、4或5个字符的字符串 43 | * `2` 多于5个字符的字符串 44 | 45 | 当然, 你可能发现编辑距离为`2` 仍然是太过了, 返回的结果好像并没有什么关联. 46 | 把 `fuzziness` 设置为 `1` ,你可能会获得更好的结果和性能. 47 | -------------------------------------------------------------------------------- /270_Fuzzy_matching/40_Fuzzy_match_query.md: -------------------------------------------------------------------------------- 1 | [[fuzzy-match-query]] 2 | === Fuzzy match Query 3 | 4 | The `match` query supports ((("typoes and misspellings", "fuzzy match query")))((("match query", "fuzzy matching")))((("fuzzy matching", "match query")))fuzzy matching out of the box: 5 | 6 | [source,json] 7 | ----------------------------------- 8 | GET /my_index/my_type/_search 9 | { 10 | "query": { 11 | "match": { 12 | "text": { 13 | "query": "SURPRIZE ME!", 14 | "fuzziness": "AUTO", 15 | "operator": "and" 16 | } 17 | } 18 | } 19 | } 20 | ----------------------------------- 21 | 22 | The query string is first analyzed, to produce the terms `[surprize, me]`, and 23 | then each term is fuzzified using the specified `fuzziness`. 24 | 25 | Similarly, the `multi_match` query also ((("multi_match queries", "fuzziness support")))supports `fuzziness`, but only when 26 | executing with type `best_fields` or `most_fields`: 27 | 28 | [source,json] 29 | ----------------------------------- 30 | GET /my_index/my_type/_search 31 | { 32 | "query": { 33 | "multi_match": { 34 | "fields": [ "text", "title" ], 35 | "query": "SURPRIZE ME!", 36 | "fuzziness": "AUTO" 37 | } 38 | } 39 | } 40 | ----------------------------------- 41 | 42 | Both the `match` and `multi_match` queries also support the `prefix_length` 43 | and `max_expansions` parameters. 44 | 45 | TIP: Fuzziness works only with the basic `match` and `multi_match` queries. It 46 | doesn't work with phrase matching, common terms, or `cross_fields` matches. 47 | 48 | -------------------------------------------------------------------------------- /270_Fuzzy_matching/50_Scoring_fuzziness.md: -------------------------------------------------------------------------------- 1 | [[fuzzy-scoring]] 2 | === Scoring Fuzziness 3 | 4 | Users love fuzzy queries. They assume that these queries will somehow magically find 5 | the right combination of proper spellings.((("fuzzy queries", "scoring fuzziness")))((("typoes and misspellings", "scoring fuzziness")))((("relevance scores", "fuzziness and"))) Unfortunately, the truth is 6 | somewhat more prosaic. 7 | 8 | Imagine that we have 1,000 documents containing ``Schwarzenegger,'' and just 9 | one document with the misspelling ``Schwarzeneger.'' According to the theory 10 | of <>, the misspelling is 11 | much more relevant than the correct spelling, because it appears in far fewer 12 | documents! 13 | 14 | In other words, if we were to treat fuzzy matches((("match query", "fuzzy match query"))) like any other match, we 15 | would favor misspellings over correct spellings, which would make for grumpy 16 | users. 17 | 18 | TIP: Fuzzy matching should not be used for scoring purposes--only to widen 19 | the net of matching terms in case there are misspellings. 20 | 21 | By default, the `match` query gives all fuzzy matches the constant score of 1. 22 | This is sufficient to add potential matches onto the end of the result list, 23 | without interfering with the relevance scoring of nonfuzzy queries. 24 | 25 | [TIP] 26 | ================================================== 27 | 28 | Fuzzy queries alone are much less useful than they initially appear. They are 29 | better used as part of a ``bigger'' feature, such as the _search-as-you-type_ 30 | http://bit.ly/1IChV5j[`completion` suggester] or the 31 | _did-you-mean_ http://bit.ly/1IOj5ZG[`phrase` suggester]. 32 | 33 | ================================================== 34 | -------------------------------------------------------------------------------- /300_Aggregations/05_overview.md: -------------------------------------------------------------------------------- 1 | [[aggregations]] 2 | = Aggregations 3 | 4 | [partintro] 5 | -- 6 | Up until this point, this book has been dedicated to search. With search, 7 | we have a query and we wish to find a subset of documents which 8 | match the query. We are looking for the proverbial needle(s) in the 9 | haystack. 10 | 11 | With aggregations, we zoom out to get an overview of our data. Instead of 12 | looking for individual documents, we want to analyze and summarize our complete 13 | set of data. 14 | 15 | // Popular manufacturers? Unusual clumps of needles in the haystack? 16 | - How many needles are in the haystack? 17 | - What is the average length of the needles? 18 | - What is the median length of needle broken down by manufacturer? 19 | - How many needles were added to the haystack each month? 20 | 21 | Aggregations can answer more subtle questions too, such as 22 | 23 | - What are your most popular needle manufacturers? 24 | - Are there any unusual or anomalous clumps of needles? 25 | 26 | Aggregations allow us to ask sophisticated questions of our data. And yet, while 27 | the functionality is completely different from search, it leverages the 28 | same data-structures. This means aggregations execute quickly and are 29 | _near-realtime_, just like search. 30 | 31 | This is extremely powerful for reporting and dashboards. Instead of performing 32 | "rollups" of your data (_e.g. that crusty hadoop job that takes a week to run_), 33 | you can visualize your data in realtime, allowing you to respond immediately. 34 | 35 | // Perhaps mention "not precalculated, out of date, and irrelevant"? 36 | // Perhaps "aggs are calculated in the context of the user's search, so you're not showing them that you have 10 4 star hotels on your site, but that you have 10 4 star hotels that *match their criteria*". 37 | Finally, aggregations operate alongside search requests. This means you can 38 | both search/filter documents _and_ perform analytics at the same time, on the 39 | same data, in a single request. And because aggregations are calculated in the 40 | context of a user's search, you're not just displaying a count of four-star hotels... 41 | you're displaying a count of four-star hotels that _match their search criteria_. 42 | 43 | Aggregations are so powerful that many companies have built large Elasticsearch 44 | clusters solely for analytics. 45 | -- 46 | -------------------------------------------------------------------------------- /300_Aggregations/10_facets.md: -------------------------------------------------------------------------------- 1 | 2 | === What about Facets? 3 | 4 | If you've used Elasticsearch in the past, you are probably aware of _facets_. 5 | You can think of Aggregations as "facets on steroids". Everything you can do 6 | with facets, you can do with aggregations. 7 | 8 | But there are plenty of operations that are possible in aggregations which are 9 | simply impossible with facets. 10 | 11 | Facets have not been officially depreciated yet, but you can expect that to 12 | happen eventually. We recommend migrating your facets over to aggregations when 13 | you get the chance, and starting all new projects with aggregations instead of facets. -------------------------------------------------------------------------------- /310_Geopoints/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 地理坐标点 -------------------------------------------------------------------------------- /310_Geopoints/20_Geopoints.md: -------------------------------------------------------------------------------- 1 | ## 地理坐标点 2 | 3 | _地理坐标点(geo-point)_ 是指地球表面可以用经纬度描述的一个点。地理坐标点可以用来计算两个坐标位置间的距离,或者判断一个点是否在一个区域中。 4 | 5 | 地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 `geo_point` 。 6 | 7 | ```json 8 | PUT /attractions 9 | { 10 | "mappings": { 11 | "restaurant": { 12 | "properties": { 13 | "name": { 14 | "type": "string" 15 | }, 16 | "location": { 17 | "type": "geo_point" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | ### 经纬度坐标格式 26 | 27 | 如上例,`location` 被声明为 `geo_point` 后,我们就可以索引包含了经纬度信息的文档了。经纬度信息的形式可以是字符串,数组或者对象。 28 | 29 | ```json 30 | PUT /attractions/restaurant/1 31 | { 32 | "name": "Chipotle Mexican Grill", 33 | "location": "40.715, -74.011" <1> 34 | } 35 | 36 | PUT /attractions/restaurant/2 37 | { 38 | "name": "Pala Pizza", 39 | "location": { <2> 40 | "lat": 40.722, 41 | "lon": -73.989 42 | } 43 | } 44 | 45 | PUT /attractions/restaurant/3 46 | { 47 | "name": "Mini Munchies Pizza", 48 | "location": [ -73.983, 40.719 ] <3> 49 | } 50 | ``` 51 | - <1> 以半角逗号分割的字符串形式 `"lat,lon"`; 52 | - <2> 明确以 `lat` 和 `lon` 作为属性的对象; 53 | - <3> 数组形式表示 `[lon,lat]`。 54 | 55 | 56 | > 注意 57 | 58 | > 可能所有人都至少踩过一次这个坑:地理坐标点用字符串形式表示时是纬度在前,经度在后(`"latitude,longitude"`),而数组形式表示时刚好相反,是经度在前,纬度在后(`[longitude,latitude]`)。 59 | 60 | > 其实,在 Elasticesearch 内部,不管字符串形式还是数组形式,都是纬度在前,经度在后。不过早期为了适配 GeoJSON 的格式规范,调整了数组形式的表示方式。 61 | 62 | > 因此,在使用地理位置(geolocation)的路上就出现了这么一个“捕熊器”,专坑那些不了解这个陷阱的使用者。 63 | -------------------------------------------------------------------------------- /310_Geopoints/30_Filter_by_geopoint.md: -------------------------------------------------------------------------------- 1 | ## 通过地理坐标点过滤 2 | 3 | 有四种地理坐标点相关的过滤方式可以用来选中或者排除文档: 4 | 5 | - `geo_bounding_box`:: 6 | 7 | 找出落在指定矩形框中的坐标点 8 | 9 | - `geo_distance`:: 10 | 11 | 找出与指定位置在给定距离内的点 12 | 13 | - `geo_distance_range`:: 14 | 15 | 找出与指定点距离在给定最小距离和最大距离之间的点 16 | 17 | - `geo_polygon`:: 18 | 19 | 找出落在多边形中的点。_这个过滤器使用代价很大_。当你觉得自己需要使用它,最好先看看 [geo-shapes](/geo-shapes) 20 | 21 | 所有这些过滤器的工作方式都相似: 22 | 把 _索引中所有文档_(而不仅仅是查询中匹配到的部分文档,见 [fielddata-intro](/fielddata-intro))的经纬度信息都载入内存,然后每个过滤器执行一个轻量级的计算去判断当前点是否落在指定区域。 23 | 24 | > 提示 25 | 26 | > 地理坐标过滤器使用代价昂贵 —— 所以最好在文档集合尽可能少的场景使用。 27 | > 你可以先使用那些简单快捷的过滤器,比如 `term` 或者 `range`,来过滤掉尽可能多的文档,最后才交给地理坐标过滤器处理。 28 | 29 | > 布尔型过滤器(`bool filter`)会自动帮你做这件事。 30 | > 它会优先让那些基于“bitset”的简单过滤器(见 [filter-caching](/filter-caching))来过滤掉尽可能多的文档,然后依次才是地理坐标过滤器或者脚本类的过滤器。 31 | 32 | -------------------------------------------------------------------------------- /310_Geopoints/32_Bounding_box.md: -------------------------------------------------------------------------------- 1 | ## 地理坐标盒模型过滤器 2 | 3 | 这是目前为止最有效的 地理坐标过滤器了,因为它计算起来非常简单。 4 | 你指定一个矩形的 `顶部`(`top`), `底部`(`bottom`), `左边界`(`left`), 和 `右边界`(`right`), 5 | 然后它只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间。(译注:原文似乎写反了) 6 | 7 | ```json 8 | 9 | GET /attractions/restaurant/_search 10 | { 11 | "query": { 12 | "filtered": { 13 | "filter": { 14 | "geo_bounding_box": { 15 | "location": { <1> 16 | "top_left": { 17 | "lat": 40.8, 18 | "lon": -74.0 19 | }, 20 | "bottom_right": { 21 | "lat": 40.7, 22 | "lon": -73.0 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | ``` 31 | - <1> 盒模型信息也可以用 `bottom_left`(左下方点)和 `top_right`(右上方点) 来表示。 32 | 33 | ### 优化盒模型 34 | 35 | `地理坐标盒模型过滤器`不需要把所有坐标点都加载到内存里。 36 | 因为它要做的只是简单判断 `纬度` 和 `经度` 坐标数值是否在给定的范围内,所以它可以用倒排索引来做一个范围(`range`)过滤。 37 | 38 | 要使用这种优化方式,需要把 `geo_point` 字段用 `纬度`(`lat`)和`经度`(`lon`)方式表示并分别索引。 39 | 40 | 41 | ```json 42 | PUT /attractions 43 | { 44 | "mappings": { 45 | "restaurant": { 46 | "properties": { 47 | "name": { 48 | "type": "string" 49 | }, 50 | "location": { 51 | "type": "geo_point", 52 | "lat_lon": true <1> 53 | } 54 | } 55 | } 56 | } 57 | } 58 | ``` 59 | - <1> `location.lat` 和 `location.lon` 字段将被分别索引。它们可以被用于检索,但是不会在检索结果中返回。 60 | 61 | 然后,查询时你需要告诉 Elasticesearch 使用已索引的 `lat`和`lon`。 62 | 63 | ```json 64 | 65 | GET /attractions/restaurant/_search 66 | { 67 | "query": { 68 | "filtered": { 69 | "filter": { 70 | "geo_bounding_box": { 71 | "type": "indexed", <1> 72 | "location": { 73 | "top_left": { 74 | "lat": 40.8, 75 | "lon": -74.0 76 | }, 77 | "bottom_right": { 78 | "lat": 40.7, 79 | "lon": -73.0 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | - <1> 设置 `type` 参数为 `indexed` (默认为 `memory`) 来明确告诉 Elasticsearch 对这个过滤器使用倒排索引。 89 | 90 | > 注意: 91 | 92 | > `geo_point` 类型可以包含多个地理坐标点,但是针对经度纬度分别索引的这种优化方式(`lat_lon`)只对单个坐标点的方式有效。 93 | 94 | 95 | -------------------------------------------------------------------------------- /310_Geopoints/36_Caching_geofilters.md: -------------------------------------------------------------------------------- 1 | ## 缓存地理位置过滤器 2 | 3 | 因为如下两个原因,地理位置过滤器默认是不被缓存的: 4 | 5 | - 地理位置过滤器通常是用于查找用户当前位置附近的东西。但是用户是在移动的,并且没有两个用户的位置完全相同,因此缓存的过滤器基本不会被重复使用到。 6 | 7 | - 过滤器是被缓存为比特位集合来表示段([segment](dynamic-indices))内的文档。假如我们的查询排除了几乎所有文档,只剩一个保存在这个特别的段内。一个未缓存的地理位置过滤器只需要检查这一个文档就行了,但是一个缓存的地理位置过滤器则需要检查所有在段内的文档。 8 | 9 | 缓存对于地理位置过滤器也可以很有效。 10 | 假设你的索引里包含了所有美国的宾馆。一个在纽约的用户是不会对旧金山的宾馆感兴趣的。 11 | 所以我们可以认为纽约是一个_热点_(_hot spot_),然后画一个边框把它和附近的区域围起来。 12 | 13 | 如果这个`地理盒模型过滤器`(`geo_bounding_box`)被缓存起来,那么当有位于纽约市的用户访问时它就可以被重复使用了。 14 | 它可以直接排除国内其它区域的宾馆。然后我们使用未缓存的,更加明确的`地理盒模型过滤器`(`geo_bounding_box`)或者`地理距离过滤器`(`geo_distance`)来在剩下的结果集中把范围进一步缩小到用户附近: 15 | 16 | 17 | ```json 18 | 19 | GET /attractions/restaurant/_search 20 | { 21 | "query": { 22 | "filtered": { 23 | "filter": { 24 | "bool": { 25 | "must": [ 26 | { 27 | "geo_bounding_box": { 28 | "type": "indexed", 29 | "_cache": true, <1> 30 | "location": { 31 | "top_left": { 32 | "lat": 40,8, 33 | "lon": -74.1 34 | }, 35 | "bottom_right": { 36 | "lat": 40.4, 37 | "lon": -73.7 38 | } 39 | } 40 | } 41 | }, 42 | { 43 | "geo_distance": { <2> 44 | "distance": "1km", 45 | "location": { 46 | "lat": 40.715, 47 | "lon": -73.988 48 | } 49 | } 50 | } 51 | ] 52 | } 53 | } 54 | } 55 | } 56 | } 57 | ``` 58 | - <1> 缓存的`地理盒模型过滤器`把结果集缩小到了纽约市。 59 | - <2> 代价更高的`地理距离过滤器`(`geo_distance`)让结果集缩小到1km内的用户。 60 | 61 | 62 | -------------------------------------------------------------------------------- /310_Geopoints/38_Reducing_memory.md: -------------------------------------------------------------------------------- 1 | ## 减少内存占用 2 | 3 | 每一个 `经纬度`(`lat/lon`)组合需要占用16个字节的内存。要知道内存可是供不应求的。 4 | 使用这种占用16字节内存的方式可以得到非常精确的结果。不过就像之前提到的一样,实际应用中几乎都不需要这么精确。 5 | 6 | 你可以通过这种方式来减少内存使用量: 7 | 设置一个`压缩的`(`compressed`)数据字段格式并明确指定你的地理坐标点所需的精度。 8 | 即使只是将精度降低到`1毫米`(`1mm`)级别,也可以减少1/3的内存使用。 9 | 更实际的,将精度设置到`3米`(`3m`)内存占用可以减少62%,而设置到`1公里`(`1km`)则节省75%之多。 10 | 11 | 这个设置项可以通过 `update-mapping` API 来对实时索引进行调整: 12 | 13 | ```json 14 | POST /attractions/_mapping/restaurant 15 | { 16 | "location": { 17 | "type": "geo_point", 18 | "fielddata": { 19 | "format": "compressed", 20 | "precision": "1km" <1> 21 | } 22 | } 23 | } 24 | ``` 25 | - <1> 每一个`经纬度`(`lat/lon`)组合现在只需要4个字节,而不是16个。 26 | 27 | 另外,你还可以这样做来避免把所有地理坐标点全部同时加载到内存中: 28 | 使用在优化盒模型([optimize-bounding-box](optimize-bounding-box))中提到的技术, 29 | 或者把地理坐标点当作文档值([doc values](doc-values))来存储。 30 | 31 | ```json 32 | 33 | PUT /attractions 34 | { 35 | "mappings": { 36 | "restaurant": { 37 | "properties": { 38 | "name": { 39 | "type": "string" 40 | }, 41 | "location": { 42 | "type": "geo_point", 43 | "doc_values": true <1> 44 | } 45 | } 46 | } 47 | } 48 | } 49 | ``` 50 | - <1> 地理坐标点现在不会被加载到内存,而是保存在磁盘中。 51 | 52 | 53 | 将地理坐标点映射为文档值的方式只能是在这个字段第一次被创建时。 54 | 相比使用字段值,使用文档值会有一些小的性能代价,不过考虑到它对内存的节省,这种方式通常是还值得的。 55 | -------------------------------------------------------------------------------- /310_Geopoints/50_Sorting_by_distance.md: -------------------------------------------------------------------------------- 1 | ## 按距离排序 2 | 3 | 检索结果可以按跟指定点的距离排序: 4 | 5 | > 提示 6 | > 当你_可以_(_can_)按距离排序时,按距离打分([scoring-by-distance](#scoring-by-distance))通常是一个更好的解决方案。 7 | 8 | ```json 9 | GET /attractions/restaurant/_search 10 | { 11 | "query": { 12 | "filtered": { 13 | "filter": { 14 | "geo_bounding_box": { 15 | "type": "indexed", 16 | "location": { 17 | "top_left": { 18 | "lat": 40,8, 19 | "lon": -74.0 20 | }, 21 | "bottom_right": { 22 | "lat": 40.4, 23 | "lon": -73.0 24 | } 25 | } 26 | } 27 | } 28 | } 29 | }, 30 | "sort": [ 31 | { 32 | "_geo_distance": { 33 | "location": { <1> 34 | "lat": 40.715, 35 | "lon": -73.998 36 | }, 37 | "order": "asc", 38 | "unit": "km", <2> 39 | "distance_type": "plane" <3> 40 | } 41 | } 42 | ] 43 | } 44 | ``` 45 | - <1> 计算每个文档中 `location` 字段与指定的 `lat/lon` 点间的距离。 46 | - <2> 以 `公里`(`km`)为单位,将距离设置到每个返回结果的 `sort` 键中。 47 | - <3> 使用快速但精度略差的`平面`(`plane`)计算方式。 48 | 49 | 你可能想问:为什么要制定距离的`单位`(`unit`)呢? 50 | 用于排序的话,我们并不关心比较距离的尺度是英里,公里还是光年。 51 | 原因是,这个用于排序的值会设置在每个返回结果的 `sort` 元素中。 52 | 53 | ```json 54 | 55 | ... 56 | "hits": [ 57 | { 58 | "_index": "attractions", 59 | "_type": "restaurant", 60 | "_id": "2", 61 | "_score": null, 62 | "_source": { 63 | "name": "New Malaysia", 64 | "location": { 65 | "lat": 40.715, 66 | "lon": -73.997 67 | } 68 | }, 69 | "sort": [ 70 | 0.08425653647614346 <1> 71 | ] 72 | }, 73 | ... 74 | ``` 75 | - <1> 宾馆距离我们的指定位置距离是 0.084km。 76 | - 你可以通过设置`单位`(`unit`)来让返回值的形式跟你应用中想要的匹配。 77 | 78 | > 提示 79 | 80 | > 地理距离排序可以对多个坐标点来使用,不管(这些坐标点)是在文档中还是排序参数中。 81 | > 使用 `sort_mode` 来指定是否需要使用位置集合的 `最小`(`min`),最大(`max`)或者`平均`(`avg`)距离。 82 | > 这样就可以返回``离我的工作地和家最近的朋友``这样的结果了。 83 | 84 | 85 | ### 按距离打分 86 | 87 | 有可能距离只是决定返回结果排序的唯一重要因素,不过更常见的情况是距离会和其它因素, 88 | 比如全文检索匹配度,流行程度或者价格一起决定排序结果。 89 | 90 | 遇到这种场景你需要在查询分值计算([`function_score` query](function-score-query))中指定方式让我们把这些因子处理得到一个综合分。 91 | [decay-functions](decay-functions)中有个一个例子就是地理距离影响排序得分的。 92 | 93 | 另外按距离排序还有个缺点就是性能:需要对每一个匹配到的文档都进行距离计算。 94 | 而 `function_score`请求,在 [`rescore` phase](rescore-api)阶段有可能只需要对前 _n_ 个结果进行计算处理。 95 | 96 | -------------------------------------------------------------------------------- /320_Geohashes/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## Geohashes -------------------------------------------------------------------------------- /320_Geohashes/40_Geohashes.md: -------------------------------------------------------------------------------- 1 | ## Geohashes 2 | 3 | [Geohashes](http://en.wikipedia.org/wiki/Geohash) 是一种将 经纬度坐标对(`lat/lon`)编码成字符串的方式。 4 | 最开始这么做只是为了让地理位置在url上呈现的形式更加友好, 5 | 不过现在geohash已经变成一种在数据库中有效索引地理坐标点和地理形状的方式。 6 | 7 | Geohashes 把整个世界分为32个单元的格子--4行8列--每一个格子都用一个字母或者数字标识。 8 | 比如 `g` 这个单元覆盖了半个格林兰,冰岛的全部和大不列颠的大部分。 9 | 每一个单元还可以进一步被分解成新的32个单元,这些单元又可以继续被分解成32个更小的单元,不断重复下去。 10 | `gc` 这个单元覆盖了爱尔兰和英格兰,`gcp`覆盖了伦敦的大部分和部分南英格兰, 11 | `gcpuuz94k`是伯明翰宫的入口,精确到了约5米。 12 | 13 | 换句话说,geohash的长度越长,它的精度就越高。 14 | 如果两个geohash有一个共同的前缀,如 `gcpuuz`,就表示他们挨得很紧。 15 | 共同的前缀越长,距离就越近。 16 | 17 | 但那也就是说,两个刚好相邻的位置,会可能有完全不同的geohash。 18 | 一个实例,伦敦的 [Millenium Dome](http://en.wikipedia.org/wiki/Millennium_Dome) 的geohash是 `u10hbp`, 19 | 因为它落在了 `u`这个大单元里,而紧挨着它东边的最大的单元是 `g`。 20 | 21 | 地理坐标点可以自动关联到他们对应的 geohash。 22 | 需要注意的是,他们会被索引到了所有(各个层级)的 geohash _前缀_(_prefixes_)。 23 | 例:索引伯明翰宫的门口--坐标纬度 `51.501568`,经度`-0.141257`--会在各种尺寸精度的 geohash 上建立索引, 24 | 如下表: 25 | 26 | Geohash |Level| Dimensions 27 | ---------------|-----|--------------------- 28 | g |1 | ~ 5,004km x 5,004km 29 | gc |2 | ~ 1,251km x 625km 30 | gcp |3 | ~ 156km x 156km 31 | gcpu |4 | ~ 39km x 19.5km 32 | gcpuu |5 | ~ 4.9km x 4.9km 33 | gcpuuz |6 | ~ 1.2km x 0.61km 34 | gcpuuz9 |7 | ~ 152.8m x 152.8m 35 | gcpuuz94 |8 | ~ 38.2m x 19.1m 36 | gcpuuz94k |9 | ~ 4.78m x 4.78m 37 | gcpuuz94kk |10 | ~ 1.19m x 0.60m 38 | gcpuuz94kkp |11 | ~ 14.9cm x 14.9cm 39 | gcpuuz94kkp5 |12 | ~ 3.7cm x 1.8cm 40 | 41 | geohash单元过滤器( [`geohash_cell` filter](http://bit.ly/1DIqyex) )可以使用这些geohash前缀来找出与指定坐标点(`lat/lon`)相邻的位置。 42 | -------------------------------------------------------------------------------- /320_Geohashes/50_Geohash_mapping.md: -------------------------------------------------------------------------------- 1 | ## Geohashes 映射 2 | 3 | 首先,你需要确定你需要什么样的精度。 4 | 虽然你也可以使用12级的精度来索引所有的地理坐标点,但是你真的需要精确到数厘米的精度吗? 5 | 如果你把精度控制在一个实际一些的值,比如 `1km`,那么你可以节省大量的索引空间: 6 | 7 | ```json 8 | 9 | PUT /attractions 10 | { 11 | "mappings": { 12 | "restaurant": { 13 | "properties": { 14 | "name": { 15 | "type": "string" 16 | }, 17 | "location": { 18 | "type": "geo_point", 19 | "geohash_prefix": true, <1> 20 | "geohash_precision": "1km" <2> 21 | } 22 | } 23 | } 24 | } 25 | } 26 | ``` 27 | - <1> 将 `geohash_prefix` 设为 `true` 来告诉 Elasticsearch 使用指定精度来做 geohash 前缀索引。 28 | - <2> 精度描述可以是一个具体数字,表示 geohash 的长度,也可以是一个距离。精度设为 `1km`表示geohash长度是 `7`。 29 | 30 | 通过如上设置,geohash前缀为 1-7 的部分将被索引,所能提供的精度大约在150米。 31 | -------------------------------------------------------------------------------- /320_Geohashes/60_Geohash_cell_filter.md: -------------------------------------------------------------------------------- 1 | ## geohash单元过滤器 2 | 3 | `geohash单元`过滤器做的事情非常简单: 4 | 把经纬度坐标位置根据指定精度转换成一个geohash,然后查找落在同一个geohash中的位置--这实在是非常高效的过滤器。 5 | 6 | ```json 7 | 8 | GET /attractions/restaurant/_search 9 | { 10 | "query": { 11 | "filtered": { 12 | "filter": { 13 | "geohash_cell": { 14 | "location": { 15 | "lat": 40.718, 16 | "lon": -73.983 17 | }, 18 | "precision": "2km" <1> 19 | } 20 | } 21 | } 22 | } 23 | } 24 | ``` 25 | - <1> `precision` 字段设置的精度不能高于geohash精度映射时的设定。 26 | 27 | 这个过滤器将坐标点转换成对应长度的geohash--本例中为`dr5rsk`--然后查找位于同一个组中的所有位置。 28 | 29 | 然而,如上例中的写法可能不会返回5km内所有的宾馆。 30 | 要知道每个 geohash 实际上仅是一个矩形,而指定的点可能位于这个矩形中的任何位置。 31 | 有可能这个点刚好落在了geohash单元的边缘附近,但过滤器会排除那些(挨得很近却)落在相邻单元里的宾馆。 32 | 33 | 为了修正这点,我们可以告诉过滤器,把周围的单元也包含进来。 34 | 通过设置`neighbors` 参数为 `true`: 35 | 36 | ```json 37 | 38 | GET /attractions/restaurant/_search 39 | { 40 | "query": { 41 | "filtered": { 42 | "filter": { 43 | "geohash_cell": { 44 | "location": { 45 | "lat": 40.718, 46 | "lon": -73.983 47 | }, 48 | "neighbors": true, <1> 49 | "precision": "2km" 50 | } 51 | } 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | - <1> 过滤器将会查找对应的geohash和包围它的(8个)geohash。 58 | 59 | 明显的,`2km`精度的geohash再加上周围的单元,会导致结果实际在一个更大的检索范围。 60 | 这个过滤器不是为精度而生的,但是它非常有效率,可以用于更高精度的地理位置过滤器的前置过滤器。 61 | 62 | > 提示 63 | 64 | > 将 `precision` 参数设置为一个距离可能会有误导性。 65 | > 比如将 `precision` 设置为 `2km` 将会转换成长度为6的geohash。但实际上它的尺寸是约 1.2km * 0.6 km。 66 | > 你可能会发现这还不如自己明确的设置一个长度 `5` 或者 `6` 来得更容易理解。 67 | 68 | 这个过滤器有一个比`地理盒模型过滤器`(`geo_bounding_box`)更好的优点,就是它支持一个字段中有多个坐标位置的情况。 69 | 我们在设置优化盒模型过滤器( [optimize-bounding-box](optimize-bounding-box) )讲过,设置 `lat_lon` 选项也是一个很有效的方式, 70 | 但是它只对字段中的单个坐标点情况有效。 71 | 72 | 73 | -------------------------------------------------------------------------------- /330_Geo_aggs/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 地理位置聚合 -------------------------------------------------------------------------------- /330_Geo_aggs/60_Geo_aggs.md: -------------------------------------------------------------------------------- 1 | ## 地理位置聚合 2 | 3 | 虽然地理位置过滤或评分功能很有用,不过更有用得是将信息再地图上呈现给用户。 4 | 检索的结果集可能很多而不能将每个点都一一呈现,这时候就可以使用地理位置聚合来把这些位置点分布到更加可控的桶(buckets)里。 5 | 6 | 7 | 有三种聚合器可以作用于 `geo_point` 类型的字段: 8 | 9 | - [`geo_distance`](geo-distance-agg) 10 | 11 | 将文档按以指定中心点为圆心的圆环分组 12 | 13 | - [`geohash_grid`](geohash-grid-agg) 14 | 15 | 将文档按 geohash 单元分组,以便在地图上呈现 16 | 17 | - [`geo_bounds`](geo-bounds-agg) 18 | 19 | 返回包含一系列矩形框的经纬坐标对,这些矩形框包含了所有的坐标点。 20 | 这种方式对于要在地图上选择一个合适的缩放等级(zoom level)时很实用。 21 | -------------------------------------------------------------------------------- /330_Geo_aggs/64_Geohash_grid_agg.md: -------------------------------------------------------------------------------- 1 | ## geohash单元聚合器 2 | 3 | 一个查询返回的结果集中可能包含很多的点,以至于不能在地图上全部单独显示。 4 | geohash单元聚合器可以按照你指定的精度计算每个点的geohash并将相邻的点聚合到一起。 5 | 6 | 返回结果是一个个单元格,每个单元格对应一个可以在地图上展示的 geohash。 7 | 通过改变 geohash 的精度,你可以统计全球、某个国家,或者一个城市级别的综述信息。 8 | 9 | 聚合结果是稀疏(_sparse_)的,因为它只返回包含了文档集合的单元。 10 | 如果你的geohash精度太细,导致生成了太多的结果集,它默认只会返回包含结果最多的10000个单元 -- 它们包含了大部分文档集合。 11 | 然后,为了找出这排在前10000的单元,它还是需要先生成所有的结果集。 12 | 你可以通过如下方式控制生成的单元的数目: 13 | 14 | - 1. 使用一个矩形过滤器来限制结果集。 15 | - 2. 对应该矩形,选择一个合适的精度。 16 | 17 | ```json 18 | 19 | GET /attractions/restaurant/_search?search_type=count 20 | { 21 | "query": { 22 | "filtered": { 23 | "filter": { 24 | "geo_bounding_box": { 25 | "location": { <1> 26 | "top_left": { 27 | "lat": 40,8, 28 | "lon": -74.1 29 | }, 30 | "bottom_right": { 31 | "lat": 40.4, 32 | "lon": -73.7 33 | } 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "aggs": { 40 | "new_york": { 41 | "geohash_grid": { <2> 42 | "field": "location", 43 | "precision": 5 44 | } 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | - <1> 矩形框将检索限制在纽约区域。 51 | - <2> 使用精度为 `5` 的geohash,精度大约是 5km x 5km. 52 | 53 | 54 | 每个精度为 `5` 的 geohash 覆盖约 25平方公里,那 10000 个单元就能覆盖 25万平方公里。 55 | 我们指定的矩形框覆盖面积约 44km * 33km,也就是大概 1452平方公里。 56 | 所以这肯定在一个安全的限度内,我们不会因此浪费大量内存来生成太多单元。 57 | 58 | 上例请求的返回如下: 59 | 60 | ```json 61 | 62 | ... 63 | "aggregations": { 64 | "new_york": { 65 | "buckets": [ <1> 66 | { 67 | "key": "dr5rs", 68 | "doc_count": 2 69 | }, 70 | { 71 | "key": "dr5re", 72 | "doc_count": 1 73 | } 74 | ] 75 | } 76 | } 77 | ... 78 | ``` 79 | 80 | - <1> 每个单元以一个 geohash 作为 `key`。 81 | 82 | Again, we didn't specify any subaggregations, so all we got back was the 83 | document count. We could have asked for popular restaurant types, average 84 | price, or other details. 85 | 同样的,我们没有指定子聚合器,所以我们的返回结果是文档数目。 86 | 我们也可以(指定子聚合器来)得到流行的饭店类型,平均价格,或者其它详细信息。 87 | 88 | > 提示 89 | 90 | > 为了将这些单元放置在地图上展示,我们需要一个类库来将geohash解析为对于的矩形框或者中心点。 91 | > Javascript和一些语言中有现成的类库,不过你也可以根据 [geo-bounds-agg](/geo-bounds-agg) 的信息自己来实现。 92 | -------------------------------------------------------------------------------- /340_Geoshapes/00_Intro.md: -------------------------------------------------------------------------------- 1 | ## 地理形状 -------------------------------------------------------------------------------- /340_Geoshapes/70_Geoshapes.md: -------------------------------------------------------------------------------- 1 | ## 地理形状 2 | 3 | 地理形状(geo-shapes)使用一种与地理坐标点完全不同的方法。 4 | 我们在计算机屏幕上看到的圆形并不是由完美的连续的线组成的;而是用一个个连续的像素点来画出的一个近似圆。 5 | 地理形状的工作方式就与此相似。 6 | 7 | 复杂的形状 -- 比如 点集,线,多边形,多多变形,中空多边形等 -- 都是通过一个个 geohash单元来画出的。 8 | 这些形状会转化为一个被它所覆盖到的 geohash 集合。 9 | 10 | > 注意 11 | 12 | > 实际上,有两种类型的格子模式能用于地理星座: 13 | > 默认是使用我们之前讨论过的 geohash;另外还有一种是 象限4叉树(_quad trees_)。 14 | > 象限4叉树和geohash类似,只不过它每个层级都是4个单元(而不是像geohash一样的32个)。 15 | > 这种不同取决于编码方式的选择。 16 | 17 | 18 | 组成一个形状的 geohash 都作为一个组索引在一起。 19 | 有这些信息,通过查看是否有相同的geohash 单元,就可以很轻易地检查两个形状是否有交集。 20 | 21 | 22 | 地理形状有这些用处:判断查询的形状与索引的形状的关系;这些关系可能是以下之一: 23 | 24 | - `intersects`:: 25 | 26 | 查询的形状与索引形状有重叠(默认)。 27 | 28 | - `disjoint`:: 29 | 30 | 查询的形状与索引的形状完全不重叠。 31 | 32 | - `within`:: 33 | 34 | 索引的形状完全被包含在查询形状中。 35 | 36 | > 注意 37 | 38 | > 地理形状不能用语计算距离、排序、打分以及聚集。 39 | 40 | -------------------------------------------------------------------------------- /340_Geoshapes/72_Mapping_geo_shapes.md: -------------------------------------------------------------------------------- 1 | ## 映射地理形状 2 | 3 | 与 `geo_point`类型的字段相似,地理形状也需要在使用前明确映射: 4 | 5 | ```json 6 | 7 | PUT /attractions 8 | { 9 | "mappings": { 10 | "landmark": { 11 | "properties": { 12 | "name": { 13 | "type": "string" 14 | }, 15 | "location": { 16 | "type": "geo_shape" 17 | } 18 | } 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 你需要关注两个重要的设置项来调整精度(`precision`)和距离误差(`distance_error_pct`)。 25 | There are two important settings that you should consider changing `precision` and `distance_error_pct`. 26 | 27 | ### 精度 28 | 29 | 精度(`precision`)参数用来控制组成地理形状的geohash的长度。 30 | 它的默认值是 `9`,等同于尺寸在 5m*5m 的[geohash](geohash)。这个精度可能比你需要的精确得多。 31 | 32 | 精度越低,需要索引的单元就越少,检索时也会更快。当然,精度越低,地理形状的准确性就越差。 33 | 你需要决定自己的地理形状所需精度 —— 即使减少1-2个等级的精度也能带来明显的消耗缩减收益。 34 | 35 | 你可以通过指定距离的方式来定制精度 —— 比如,`50m`或`2km`; 36 | 不过这些距离最终也是会转换成对应的geohash长度(见 [geohashes](geohashes))。 37 | 38 | 39 | ### 距离误差 40 | 41 | 当索引一个多边形时,中间连续区域很容易用一个短geohash来表示。 42 | 麻烦的是边缘部分,这些地方需要使用更精细的geohash才能表示。 43 | 44 | 当你在索引一个小地标时,你希望它的边界比较精确;(如果精度不够,)让这些纪念碑一个叠着一个可不好。 45 | 当索引整个国家时,你就不需要这么高的精度。误差个50米左右也没什么大不了。 46 | 47 | 48 | 距离误差(`distance_error_pct`)指定地理形状可以接受的最大错误率。它的默认值是 `0.025`,即 2.5%。 49 | 这也就是说,大的地理形状(比如国家)相比小的地理形状(比如纪念碑)来说,容许更加模糊的边界。 50 | 51 | `0.025`是一个不错的初始值。不过如果我们容许更大的错误率,对应地理形状需要索引的单元就越少。 52 | -------------------------------------------------------------------------------- /340_Geoshapes/74_Indexing_geo_shapes.md: -------------------------------------------------------------------------------- 1 | ## 索引地理形状 2 | 3 | 地理形状通过[GeoJSON](http://geojson.org)来表示,这是一种开放的使用JSON实现的二维形状编码方式。 4 | 每个形状包含两个信息:形状类型:`point`, `line`, `polygon`, `envelope`;一个或多经纬度点集合的数组。 5 | 6 | 7 | > 注意: 8 | > 9 | > 在 GeoJSON 里,经纬度表示方式通常是“纬度在前,经度在后”。 10 | 11 | 12 | 举例如下,我们用一个多边形来索引阿姆斯特丹达姆广场: 13 | 14 | ```json 15 | 16 | PUT /attractions/landmark/dam_square 17 | { 18 | "name" : "Dam Square, Amsterdam", 19 | "location" : { 20 | "type" : "polygon", <1> 21 | "coordinates" : [[ <2> 22 | [ 4.89218, 52.37356 ], 23 | [ 4.89205, 52.37276 ], 24 | [ 4.89301, 52.37274 ], 25 | [ 4.89392, 52.37250 ], 26 | [ 4.89431, 52.37287 ], 27 | [ 4.89331, 52.37346 ], 28 | [ 4.89305, 52.37326 ], 29 | [ 4.89218, 52.37356 ] 30 | ]] 31 | } 32 | } 33 | ``` 34 | 35 | - <1> `type`参数指明如何使用经纬度坐标集来表示对应形状。 36 | - <2> 用来表示多边形的经纬度坐标点列表。 37 | 38 | 上例中大量的方括号可能看起来让人困惑,不过实际上 GeoJSON 的语法非常简单: 39 | 40 | 1. 用一个数组表示经纬度坐标点: 41 | ```json 42 | [lon,lat] 43 | ``` 44 | 45 | 2. 一组坐标点放到一个数组来表示一个多边形: 46 | ```json 47 | [[lon,lat],[lon,lat], ... ] 48 | ``` 49 | 50 | 3. 一个多边形(`polygon`)形状可以包含多个多边形;第一个表示多边形的外轮廓,后续的多边形表示第一个多边形内部的空洞: 51 | ```json 52 | [ 53 | [[lon,lat],[lon,lat], ... ], # main polygon 54 | [[lon,lat],[lon,lat], ... ], # hole in main polygon 55 | ... 56 | ] 57 | ``` 58 | 59 | 参见 [Geo-shape mapping documentation](http://bit.ly/1G2nMCT) 了解更多支持的形状。 60 | 61 | -------------------------------------------------------------------------------- /340_Geoshapes/76_Querying_geo_shapes.md: -------------------------------------------------------------------------------- 1 | ## 查询地理形状 2 | 3 | 地理形状一个不寻常的地方在于它运行我们使用形状来做查询,而不仅仅是坐标点。 4 | 5 | 举个例子,当我们的用户刚刚迈出阿姆斯特丹中央火车站时,我们可以用如下方式,查询出方圆1km内所有的地标: 6 | 7 | ```json 8 | 9 | GET /attractions/landmark/_search 10 | { 11 | "query": { 12 | "geo_shape": { 13 | "location": { <1> 14 | "shape": { <2> 15 | "type": "circle", <3> 16 | "radius": "1km" 17 | "coordinates": [ <4> 18 | 4.89994, 19 | 52.37815 20 | ] 21 | } 22 | } 23 | } 24 | } 25 | } 26 | ``` 27 | 28 | - <1> 查询使用 `location` 字段中的地理形状; 29 | - <2> 查询中的形状是由`shape`键对应的内容表示; 30 | - <3> 形状是一个半径为1km的圆形; 31 | - <4> 安姆斯特丹中央火车站入口的坐标点。 32 | 33 | 默认,查询(或者过滤器 —— 工作方式相同)会从已索引的形状中寻找与指定形状有交集的形状。 34 | 此外,`relation` 也可以设置为 `disjoint`来查找与指定形状不相交的,或者设置为`within`来查找完全落在查询形状中的。 35 | 36 | 37 | 举个例子,我们查找所有落在阿姆斯特丹内的地标: 38 | 39 | ```json 40 | 41 | GET /attractions/landmark/_search 42 | { 43 | "query": { 44 | "geo_shape": { 45 | "location": { 46 | "relation": "within", <1> 47 | "shape": { 48 | "type": "polygon", 49 | "coordinates": [[ <2> 50 | [4.88330,52.38617], 51 | [4.87463,52.37254], 52 | [4.87875,52.36369], 53 | [4.88939,52.35850], 54 | [4.89840,52.35755], 55 | [4.91909,52.36217], 56 | [4.92656,52.36594], 57 | [4.93368,52.36615], 58 | [4.93342,52.37275], 59 | [4.92690,52.37632], 60 | [4.88330,52.38617] 61 | ]] 62 | } 63 | } 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | - <1> 只匹配完全落在查询形状中的(已索引)形状。 70 | - <2> 这个多边形表示安姆斯特丹中心。 71 | 72 | -------------------------------------------------------------------------------- /340_Geoshapes/78_Indexed_geo_shapes.md: -------------------------------------------------------------------------------- 1 | ## 在查询中使用已索引的形状 2 | 3 | 对于那些经常会在查询中使用的形状,可以把它们索引起来以便在查询中可以方便地直接引用名字。 4 | 以之前的阿姆斯特丹中央为例,我们可以把它存储为一个类型为 `neighborhood` 的文档。 5 | 6 | 首先,我们仿照之前设置 `landmark` 时的方式建立一个映射: 7 | 8 | ```json 9 | 10 | PUT /attractions/_mapping/neighborhood 11 | { 12 | "properties": { 13 | "name": { 14 | "type": "string" 15 | }, 16 | "location": { 17 | "type": "geo_shape" 18 | } 19 | } 20 | } 21 | ``` 22 | 23 | 然后我们索引阿姆斯特丹中央对应的形状: 24 | 25 | ```json 26 | 27 | PUT /attractions/neighborhood/central_amsterdam 28 | { 29 | "name" : "Central Amsterdam", 30 | "location" : { 31 | "type" : "polygon", 32 | "coordinates" : [[ 33 | [4.88330,52.38617], 34 | [4.87463,52.37254], 35 | [4.87875,52.36369], 36 | [4.88939,52.35850], 37 | [4.89840,52.35755], 38 | [4.91909,52.36217], 39 | [4.92656,52.36594], 40 | [4.93368,52.36615], 41 | [4.93342,52.37275], 42 | [4.92690,52.37632], 43 | [4.88330,52.38617] 44 | ]] 45 | } 46 | } 47 | ``` 48 | 49 | 形状索引好之后,我们就可以在查询中通过 `index`, `type` 和 `id` 来引用它了: 50 | 51 | ```json 52 | 53 | GET /attractions/landmark/_search 54 | { 55 | "query": { 56 | "geo_shape": { 57 | "location": { 58 | "relation": "within", 59 | "indexed_shape": { <1> 60 | "index": "attractions", 61 | "type": "neighborhood", 62 | "id": "central_amsterdam", 63 | "path": "location" 64 | } 65 | } 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | <1> 指定 `indexed_shape` 而不是 `shape`,Elasticesearch 就知道需要从指定的文档和路径检索出对应的形状了。 72 | 73 | 阿姆斯特丹中央这个形状没有什么特别的。同样地,我们也可以使用已经索引好的阿姆斯特丹达姆广场。 74 | 这个查询查找出与阿姆斯特丹达姆广场有交集的临近点: 75 | 76 | ```json 77 | 78 | GET /attractions/neighborhood/_search 79 | { 80 | "query": { 81 | "geo_shape": { 82 | "location": { 83 | "indexed_shape": { 84 | "index": "attractions", 85 | "type": "landmark", 86 | "id": "dam_square", 87 | "path": "location" 88 | } 89 | } 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | 96 | -------------------------------------------------------------------------------- /340_Geoshapes/80_Caching_geo_shapes.md: -------------------------------------------------------------------------------- 1 | ## 地理形状的过滤与缓存 2 | 3 | `地理形状` 的查询和过滤都是表现为相同功能。查询就是简单的表现为一个过滤:把所以匹配到的文档的 `_score` 标记为1。 4 | 查询结果不能被缓存,不过过滤结果可以。 5 | 6 | 结果默认是不被缓存的。与地理坐标点集类似,任何形状内坐标的变化都会导致 geohash 集合的变化,因此在缓存过滤结果几乎没有什么意义。 7 | 也就是说,除非你会重复的使用相同的形状来做过滤,它才是值得缓存起来的。 8 | 缓存方法是,把 `_cache` 设置为 `true`: 9 | 10 | ```json 11 | 12 | GET /attractions/neighborhood/_search 13 | { 14 | "query": { 15 | "filtered": { 16 | "filter": { 17 | "geo_shape": { 18 | "_cache": true, <1> 19 | "location": { 20 | "indexed_shape": { 21 | "index": "attractions", 22 | "type": "landmark", 23 | "id": "dam_square", 24 | "path": "location" 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | ``` 33 | - <1> `geo_shape` 过滤器的结果将被缓存。 34 | 35 | -------------------------------------------------------------------------------- /400_Relationships/15_Application_joins.md: -------------------------------------------------------------------------------- 1 | ## 应用级别的Join操作 2 | 3 | 我们可以在应用这一层面(部分的)模仿实现关系数据库中的join操作。例如,我们要给 `users` 以及每个 `user` 所对应的若干篇 `blog` 建立索引。在这充满关系的世界中,我们可以做一些类似于这样的事情: 4 | 5 | ``` 6 | PUT /my_index/user/1 (1) 7 | { 8 | "name": "John Smith", 9 | "email": "john@smith.com", 10 | "dob": "1970/10/24" 11 | } 12 | 13 | PUT /my_index/blogpost/2 (1) 14 | { 15 | "title": "Relationships", 16 | "body": "It's complicated...", 17 | "user": 1 (2) 18 | } 19 | ``` 20 | (1) 每一个 document 中 `index`,`type`,和 `id` 共同组成了主键。 21 | 22 | (2) `blogpost` 通过包含 `user` 的 `id` 来关联 `user`,而这里不需要指定 `user` 的 `index` 和 `type` 是因为在我们的应用中它们是被硬编码的(这里的硬编码的意思应该是说,在 `blogpost` document中引用了 `user` ,那么es就会在相同的index下查找 `user` type,并且id为1的document,所以不需要指定 `index` 和 `type`)。 23 | 24 | 通过查询 `user` 的ID为1将很容易找到相应的 `blog`: 25 | 26 | ``` 27 | GET /my_index/blogpost/_search 28 | { 29 | "query": { 30 | "filtered": { 31 | "filter": { 32 | "term": { "user": 1 } 33 | } 34 | } 35 | } 36 | } 37 | ``` 38 | 想通过博客作者的名字 `John` 来找到相关的博客,我们需要执行2个查询语句: 39 | 第一,我们需要先找到所有叫 `John` 的博客作者,从而获得它们的 ID列表, 40 | 第二,将获取到的ID列表作为查询条件来执行类似于上面的查询语句: 41 | 42 | ``` 43 | GET /my_index/user/_search 44 | { 45 | "query": { 46 | "match": { 47 | "name": "John" 48 | } 49 | } 50 | } 51 | 52 | GET /my_index/blogpost/_search 53 | { 54 | "query": { 55 | "filtered": { 56 | "filter": { 57 | "terms": { "user": [1] } (1) 58 | } 59 | } 60 | } 61 | } 62 | ``` 63 | (1) 其中 `terms` 的值被设置成从第一个查询中得到的ID列表。 64 | 65 | 在应用级别模仿join操作的最大好处是数据是立体的(normalized),如果想改变 `user` 的姓名,那么只要在 `user` 这个 document 上改就可以了。而缺点是你必须在查询期间运行额外的 query 来实现 join 的操作。 66 | 67 | 在这个例子当中,只有一个 `user` 符合我们的第一个查询条件,但在真实的世界中,很可能会出现数百万人的名字叫 `John`,将这么多的ID塞到第二个查询中,将会让这个查询语句变得非常庞大,并且这个查询会执行数百万次 `term` 的查找。 68 | 69 | 这种模仿join操作的方法适合于前置查询结果集(在该例子中指代 `user`)比较小,并且最好是不经常变化的,此时我们在应用中可以去缓存这部分数据,避免频繁的执行第一个查询。 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /400_Relationships/20_Denormalization.md: -------------------------------------------------------------------------------- 1 | ## 扁平化你的数据 2 | 3 | Elasticsearch 鼓励你在创建索引的时候就 [扁平化(denormalizing)](http://en.wikipedia.org/wiki/Denormalization) 你的数据,这样做可以获取最好的搜索性能。在每一篇文档里面冗余一些数据可以避免join操作。 4 | 5 | 举个例子,如果我们想通过 `user` 来查找某一篇 `blog`,那么就把 `user` 的姓名包含在 `blog` 这个 document 里面,就像这样: 6 | 7 | ``` json 8 | 9 | PUT /my_index/user/1 10 | { 11 | "name": "John Smith", 12 | "email": "john@smith.com", 13 | "dob": "1970/10/24" 14 | } 15 | 16 | PUT /my_index/blogpost/2 17 | { 18 | "title": "Relationships", 19 | "body": "It's complicated...", 20 | "user": { 21 | "id": 1, 22 | "name": "John Smith" (1) 23 | } 24 | } 25 | 26 | ``` 27 | 28 | (1) `user` 中的一部分信息已经被包含到 `blogpost` 里面。 29 | 30 | 31 | 现在,我们只要通过一次查询,就能找到和作者 `John` 有关系的所有博客: 32 | 33 | 34 | ``` 35 | 36 | GET /my_index/blogpost/_search 37 | { 38 | "query": { 39 | "bool": { 40 | "must": [ 41 | { "match": { "title": "relationships" }}, 42 | { "match": { "user.name": "John" }} 43 | ] 44 | } 45 | } 46 | } 47 | 48 | ``` 49 | 50 | 扁平化数据的好处就是一个字,快。因为每一篇文档都已经包含了所有需要被查询的信息,所以就没有必要去做消耗很大的join操作了。 51 | -------------------------------------------------------------------------------- /402_Nested/00_Intro.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/402_Nested/00_Intro.md -------------------------------------------------------------------------------- /402_Nested/31_Nested_mapping.md: -------------------------------------------------------------------------------- 1 | ##嵌套-映射 2 | ### 嵌套对象映射 3 | 4 | 设定一个`nested`栏位很简单--在你会设定为`object`类型的地方,改为`nested`类型: 5 | 6 | ```json 7 | PUT /my_index 8 | { 9 | "mappings": { 10 | "blogpost": { 11 | "properties": { 12 | "comments": { 13 | "type": "nested", <1> 14 | "properties": { 15 | "name": { "type": "string" }, 16 | "comment": { "type": "string" }, 17 | "age": { "type": "short" }, 18 | "stars": { "type": "short" }, 19 | "date": { "type": "date" } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | } 26 | ``` 27 | <1> 一个`nested`栏位接受与`object`类型相同的参数。 28 | 29 | 所需仅此而已。 任何`comments`对象会被索引为分离嵌套对象。 30 | 参考更多 [`nested` type reference docs](http://bit.ly/1KNQEP9)。 31 | 32 | -------------------------------------------------------------------------------- /402_Nested/32_Nested_query.md: -------------------------------------------------------------------------------- 1 | ## 嵌套-查询 2 | ### 查询嵌套对象 3 | 4 | 因嵌套对象(nested objects)会被索引为分离的隐藏文档,我们不能直接查询它们。而是使用 [`nested`查询](http://bit.ly/1ziFQoR)或 [`nested` 过滤器](http://bit.ly/1IOp94r)来存取它们: 5 | 6 | ```json 7 | GET /my_index/blogpost/_search 8 | { 9 | "query": { 10 | "bool": { 11 | "must": [ 12 | { "match": { "title": "eggs" }}, <1> 13 | { 14 | "nested": { 15 | "path": "comments", <2> 16 | "query": { 17 | "bool": { 18 | "must": [ <3> 19 | { "match": { "comments.name": "john" }}, 20 | { "match": { "comments.age": 28 }} 21 | ] 22 | }}}} 23 | ] 24 | }}} 25 | ``` 26 | <1> `title`条件运作在根文档上 27 | <2> `nested`条件``深入``嵌套的`comments`栏位。它不会在存取根文档的栏位,或是其他嵌套文档的栏位。 28 | <3> `comments.name`以及`comments.age`运作在相同的嵌套文档。 29 | 30 | >### TIP 31 | >一个`nested`栏位可以包含其他`nested`栏位。 相同的,一个`nested`查询可以包含其他`nested`查询。 32 | 嵌套阶层会如同你预期的运作。 33 | 34 | 当然,一个`nested`查询可以匹配多个嵌套文档。 35 | 每个文档的匹配会有各自的关联分数,但多个分数必须减少至单一分数才能应用至根文档。 36 | 37 | 在预设中,它会平均所有嵌套文档匹配的分数。这可以藉由设定`score_mode`参数为`avg`, `max`, `sum`或甚至`none`(为了防止根文档永远获得`1.0`的匹配分数时)来控制。 38 | 39 | ```json 40 | GET /my_index/blogpost/_search 41 | { 42 | "query": { 43 | "bool": { 44 | "must": [ 45 | { "match": { "title": "eggs" }}, 46 | { 47 | "nested": { 48 | "path": "comments", 49 | "score_mode": "max", <1> 50 | "query": { 51 | "bool": { 52 | "must": [ 53 | { "match": { "comments.name": "john" }}, 54 | { "match": { "comments.age": 28 }} 55 | ] 56 | }}}} 57 | ] 58 | }}} 59 | ``` 60 | <1> 从最匹配的嵌套文档中给予根文档的`_score`值。 61 | 62 | >### 注意 63 | >`nested`过滤器类似於`nested`查询,除了无法使用`score_mode`参数。 只能使用在_filter context_—例如在`filtered`查询中--其作用类似其他的过滤器: 64 | 包含或不包含,但不评分。 65 | 66 | >`nested`过滤器的结果本身不会缓存,通常缓存规则会被应用於`nested`过滤器_之中_的过滤器。 67 | -------------------------------------------------------------------------------- /402_Nested/33_Nested_sorting.md: -------------------------------------------------------------------------------- 1 | ## 嵌套排序 2 | ### 以嵌套栏位排序 3 | 4 | 我们可以依照嵌套栏位中的值来排序,甚至藉由分离嵌套文档中的值。为了使其结果更加有趣,我们加入另一个记录: 5 | 6 | ```json 7 | PUT /my_index/blogpost/2 8 | { 9 | "title": "Investment secrets", 10 | "body": "What they don't tell you ...", 11 | "tags": [ "shares", "equities" ], 12 | "comments": [ 13 | { 14 | "name": "Mary Brown", 15 | "comment": "Lies, lies, lies", 16 | "age": 42, 17 | "stars": 1, 18 | "date": "2014-10-18" 19 | }, 20 | { 21 | "name": "John Smith", 22 | "comment": "You're making it up!", 23 | "age": 28, 24 | "stars": 2, 25 | "date": "2014-10-16" 26 | } 27 | ] 28 | } 29 | ``` 30 | 31 | 想像我们要取回在十月中有收到回应的blog文章,并依照所取回的各个blog文章中最少`stars`数量的顺序作排序。 32 | 这个搜寻请求如下: 33 | 34 | ```json 35 | GET /_search 36 | { 37 | "query": { 38 | "nested": { <1> 39 | "path": "comments", 40 | "filter": { 41 | "range": { 42 | "comments.date": { 43 | "gte": "2014-10-01", 44 | "lt": "2014-11-01" 45 | } 46 | } 47 | } 48 | } 49 | }, 50 | "sort": { 51 | "comments.stars": { <2> 52 | "order": "asc", <2> 53 | "mode": "min", <2> 54 | "nested_filter": { <3> 55 | "range": { 56 | "comments.date": { 57 | "gte": "2014-10-01", 58 | "lt": "2014-11-01" 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | ``` 66 | <1> `nested`查询限制了结果为十月份收到回应的blog文章。 67 | <2> 结果在所有匹配的回应中依照`comment.stars`栏位的最小值(`min`)作递增(`asc`)的排序。 68 | <3> 排序条件中的`nested_filter`与主查询`query`条件中的`nested`查询相同。 於下一个下方解释。 69 | 70 | 为什么我们要在`nested_filter`重复写上查询条件? 原因是排序在於执行查询后才发生。 71 | 此查询匹配了在十月中有收到回应的blog文章,回传blog文章文档作为结果。 72 | 如果我们不加上`nested_filter`条件,我们最後会依照任何blog文章曾经收到过的回应作排序,而不是在十月份收到的。 73 | -------------------------------------------------------------------------------- /404_Parent_Child/00_Intro.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/404_Parent_Child/00_Intro.md -------------------------------------------------------------------------------- /404_Parent_Child/55_Has_parent.md: -------------------------------------------------------------------------------- 1 | [[has-parent]] 2 | === Finding Children by Their Parents 3 | 4 | While a `nested` query can always ((("parent-child relationship", "finding children by their parents")))return only the root document as a result, 5 | parent and child documents are independent and each can be queried 6 | independently. The `has_child` query allows us to return parents based on 7 | data in their children, and the `has_parent` query returns children based on 8 | data in their parents.((("has_parent query and filter", "query"))) 9 | 10 | It looks very similar to the `has_child` query. This example returns 11 | employees who work in the UK: 12 | 13 | [source,json] 14 | ------------------------- 15 | GET /company/employee/_search 16 | { 17 | "query": { 18 | "has_parent": { 19 | "type": "branch", <1> 20 | "query": { 21 | "match": { 22 | "country": "UK" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | ------------------------- 29 | <1> Returns children who have parents of type `branch` 30 | 31 | The `has_parent` query also supports the `score_mode`,((("score_mode parameter"))) but it accepts only two 32 | settings: `none` (the default) and `score`. Each child can have only one 33 | parent, so there is no need to reduce multiple scores into a single score for 34 | the child. The choice is simply between using the score (`score`) or not 35 | (`none`). 36 | 37 | .has_parent Filter 38 | ************************** 39 | 40 | The `has_parent` filter works in the same way((("has_parent query and filter", "filter"))) as the `has_parent` query, except 41 | that it doesn't support the `score_mode` parameter. It can be used only in 42 | _filter context_—such as inside a `filtered` query--and behaves 43 | like any other filter: it includes or excludes, but doesn't score. 44 | 45 | While the results of a `has_parent` filter are not cached, the usual caching 46 | rules apply to the filter _inside_ the `has_parent` filter. 47 | 48 | ************************** 49 | 50 | -------------------------------------------------------------------------------- /404_Parent_Child/60_Children_agg.md: -------------------------------------------------------------------------------- 1 | [[children-agg]] 2 | === Children Aggregation 3 | 4 | Parent-child supports a 5 | http://bit.ly/1xtpjaz[`children` aggregation] as ((("aggregations", "children aggregation")))((("children aggregation")))((("parent-child relationship", "children aggregation")))a direct analog to the `nested` aggregation discussed in 6 | <>. A parent aggregation (the equivalent of 7 | `reverse_nested`) is not supported. 8 | 9 | This example demonstrates how we could determine the favorite hobbies of our 10 | employees by country: 11 | 12 | [source,json] 13 | ------------------------- 14 | GET /company/branch/_search?search_type=count 15 | { 16 | "aggs": { 17 | "country": { 18 | "terms": { <1> 19 | "field": "country" 20 | }, 21 | "aggs": { 22 | "employees": { 23 | "children": { <2> 24 | "type": "employee" 25 | }, 26 | "aggs": { 27 | "hobby": { 28 | "terms": { <3> 29 | "field": "employee.hobby" 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | ------------------------- 39 | <1> The `country` field in the `branch` documents. 40 | <2> The `children` aggregation joins the parent documents with 41 | their associated children of type `employee`. 42 | <3> The `hobby` field from the `employee` child documents. 43 | 44 | -------------------------------------------------------------------------------- /410_Scaling/10_Intro.md: -------------------------------------------------------------------------------- 1 | ## 可扩展性规划 2 | 3 | 在某些公司Elasticsearch被用来每天索引和检索PB级别的数据,但大多情况我们是从一个相对小很多的数据集开始系统建设的。虽然我们希望能成为下一个Facebook,但是现实往往比理想更骨感。虽然我们从当下开始建设,但之后横向扩展的灵活性和简便性还是必须考虑的。 4 | 5 | ELasticsearch天生就是可扩展的。无论是运行在你的个人电脑之上还是运行在由数百个节点构成的集群之上,ELasticsearch都能很好的工作而且两者之间的使用体验并不会有太大的差异。从一个小集群扩展到一个大集群的过程几乎是自动化的、自然而然的。从一个大集群扩展到一个巨型集群会需要一点规划与设计,但是相对来说还是比较自然的。 6 | 7 | 当然,Elasticsearch也不是包治百病的灵丹妙药,它也有自身的限制。如果你了解这些限制并且能很好的规避它们,这个扩展的过程将会比较平顺。否则你不善待Elasticsearch,它也不会让你很好过。 8 | 9 | Elasticsearch的默认设置足以支撑你的系统走很长的路,但是为了更好的利用资源,你还是要认真的设计系统中的数据流。我们将会讨论两个通用的数据流场景:基于时间的数据流(比如日志事件、社交网络时间轴这些基于时间相关性的数据)和基于用户的数据流(比如大规模的数据集合可以按照用户/客户进行切分的场景)。 10 | 11 | 本章我们将帮助你在系统建设早期做出正确的决策,从而尽可能避免日后意想不到的麻烦。 12 | -------------------------------------------------------------------------------- /410_Scaling/15_Shard.md: -------------------------------------------------------------------------------- 1 | [[shard-scale]] 2 | === The Unit of Scale 3 | 4 | In <>, we explained that a shard is a _Lucene index_ and that 5 | an Elasticsearch index is a collection of shards.((("scaling", "shard as unit of scale"))) Your application talks to an 6 | index, and Elasticsearch routes your requests to the appropriate shards. 7 | 8 | A shard is the _unit of scale_. ((("shards", "as unit of scale"))) The smallest index you can have is one with a 9 | single shard. This may be more than sufficient for your needs--a single 10 | shard can hold a lot of data--but it limits your ability to scale. 11 | 12 | Imagine that our cluster consists of one node, and in our cluster we have one 13 | index, which has only one shard: 14 | 15 | [source,json] 16 | ---------------------------- 17 | PUT /my_index 18 | { 19 | "settings": { 20 | "number_of_shards": 1, <1> 21 | "number_of_replicas": 0 22 | } 23 | } 24 | ---------------------------- 25 | <1> Create an index with one primary shard and zero replica shards. 26 | 27 | This setup may be small, but it serves our current needs and is cheap to run. 28 | 29 | [NOTE] 30 | ================================================== 31 | 32 | At the moment we are talking about only _primary_ shards.((("primary shards"))) We discuss 33 | _replica_ shards in <>. 34 | 35 | ================================================== 36 | 37 | One glorious day, the Internet discovers us, and a single node just can't keep up with 38 | the traffic. We decide to add a second node, as per <>. What happens? 39 | 40 | [[img-one-shard]] 41 | .An index with one shard has no scale factor 42 | image::images/elas_4401.png["An index with one shard has no scale factor"] 43 | 44 | The answer is: nothing. Because we have only one shard, there is nothing to 45 | put on the second node. We can't increase the number of shards in the index, 46 | because the number of shards is an important element in the algorithm used to 47 | <>: 48 | 49 | shard = hash(routing) % number_of_primary_shards 50 | 51 | Our only option now is to reindex our data into a new, bigger index that has 52 | more shards, but that will take time that we can ill afford. By planning 53 | ahead, we could have avoided this problem completely by _overallocating_. 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /410_Scaling/25_Kagillion_shards.md: -------------------------------------------------------------------------------- 1 | [[kagillion-shards]] 2 | === Kagillion Shards 3 | 4 | The first thing that new users do when they learn about 5 | <> is((("scaling", "shard overallocation", "limiting")))((("shards", "overallocation of", "limiting"))) to say to themselves: 6 | 7 | [quote, A new user] 8 | _______________________________ 9 | [role="alignmeright"] 10 | I don't know how big this is going to be, and I can't change the index size 11 | later on, so to be on the safe side, I'll just give this index 1,000 shards... 12 | 13 | _______________________________ 14 | 15 | One thousand shards--really? And you don't think that, perhaps, between now 16 | and the time you need to buy _one thousand nodes_, that you may need to 17 | rethink your data model once or twice and have to reindex? 18 | 19 | A shard is not free. Remember: 20 | 21 | * A shard is a Lucene index under the covers, which uses file handles, 22 | memory, and CPU cycles. 23 | 24 | * Every search request needs to hit a copy of every shard in the index. 25 | That's fine if every shard is sitting on a different node, but not if many 26 | shards have to compete for the same resources. 27 | 28 | * Term statistics, used to calculate relevance, are per shard. Having a small 29 | amount of data in many shards leads to poor relevance. 30 | 31 | [TIP] 32 | =============================== 33 | 34 | A little overallocation is good. A kagillion shards is bad. It is difficult to 35 | define what constitutes too many shards, as it depends on their size and how 36 | they are being used. A hundred shards that are seldom used may be fine, while 37 | two shards experiencing very heavy usage could be too many. Monitor your nodes 38 | to ensure that they have enough spare capacity to deal with exceptional 39 | conditions. 40 | 41 | =============================== 42 | 43 | Scaling out should be done in phases. Build in enough capacity to get to the 44 | next phase. Once you get to the next phase, you have time to think about the 45 | changes you need to make to reach the phase after that. 46 | 47 | 48 | -------------------------------------------------------------------------------- /410_Scaling/30_Capacity_planning.md: -------------------------------------------------------------------------------- 1 | [[capacity-planning]] 2 | === Capacity Planning 3 | 4 | If 1 shard is too few and 1,000 shards are too many, how do I know how many 5 | shards I need?((("shards", "determining number you need")))((("capacity planning")))((("scaling", "capacity planning"))) This is a question that is impossible to answer in the general case. There are 6 | just too many variables: the hardware that you use, the size and complexity 7 | of your documents, how you index and analyze those documents, the types of 8 | queries that you run, the aggregations that you perform, how you model your 9 | data, and more. 10 | 11 | Fortunately, it is an easy question to answer in the specific case--yours: 12 | 13 | 1. Create a cluster consisting of a single server, with the hardware that you 14 | are considering using in production. 15 | 16 | 2. Create an index with the same settings and analyzers that you plan to use 17 | in production, but with only one primary shard and no replicas. 18 | 19 | 3. Fill it with real documents (or as close to real as you can get). 20 | 21 | 4. Run real queries and aggregations (or as close to real as you can get). 22 | 23 | Essentially, you want to replicate real-world usage and to push this single 24 | shard until it ``breaks.'' Even the definition of _breaks_ depends on you: 25 | some users require that all responses return within 50ms; others are quite 26 | happy to wait for 5 seconds. 27 | 28 | Once you define the capacity of a single shard, it is easy to extrapolate that 29 | number to your whole index. Take the total amount of data that you need to 30 | index, plus some extra for future growth, and divide by the capacity of a 31 | single shard. The result is the number of primary shards that you will need. 32 | 33 | [TIP] 34 | ================================ 35 | 36 | Capacity planning should not be your first step. 37 | 38 | First look for ways to optimize how you are using Elasticsearch. Perhaps you 39 | have inefficient queries, not enough RAM, or you have left swap enabled? 40 | 41 | We have seen new users who, frustrated by initial performance, immediately 42 | start trying to tune the garbage collector or adjust the number of threads, 43 | instead of tackling the simple problems like removing wildcard queries. 44 | 45 | ================================ 46 | -------------------------------------------------------------------------------- /410_Scaling/50_Index_templates.md: -------------------------------------------------------------------------------- 1 | [[index-templates]] 2 | === Index Templates 3 | 4 | Elasticsearch doesn't require you to create an index before using it.((("indices", "templates")))((("scaling", "index templates and")))((("templates", "index"))) With 5 | logging, it is often more convenient to rely on index autocreation than to 6 | have to create indices manually. 7 | 8 | Logstash uses the timestamp((("Logstash")))((("timestamps, use by Logstash to create index names"))) from an event to derive the index name. By 9 | default, it indexes into a different index every day, so an event with a 10 | `@timestamp` of `2014-10-01 00:00:01` will be sent to the index 11 | `logstash-2014.10.01`. If that index doesn't already exist, it will be 12 | created for us. 13 | 14 | Usually we want some control over the settings and mappings of the new index. 15 | Perhaps we want to limit the number of shards to `1`, and we want to disable the 16 | `_all` field. Index templates can be used to control which settings should be 17 | applied to newly created indices: 18 | 19 | [source,json] 20 | ------------------------- 21 | PUT /_template/my_logs <1> 22 | { 23 | "template": "logstash-*", <2> 24 | "order": 1, <3> 25 | "settings": { 26 | "number_of_shards": 1 <4> 27 | }, 28 | "mappings": { 29 | "_default_": { <5> 30 | "_all": { 31 | "enabled": false 32 | } 33 | } 34 | }, 35 | "aliases": { 36 | "last_3_months": {} <6> 37 | } 38 | } 39 | ------------------------- 40 | <1> Create a template called `my_logs`. 41 | <2> Apply this template to all indices beginning with `logstash-`. 42 | <3> This template should override the default `logstash` template that has 43 | a lower `order`. 44 | <4> Limit the number of primary shards to `1`. 45 | <5> Disable the `_all` field for all types. 46 | <6> Add this index to the `last_3_months` alias. 47 | 48 | This template specifies the default settings that will be applied to any index 49 | whose name begins with `logstash-`, whether it is created manually or 50 | automatically. If we think the index for tomorrow will need more capacity than 51 | today, we can update the index to use a higher number of shards. 52 | 53 | The template even adds the newly created index into the `last_3_months` alias, although 54 | removing the old indices from that alias will have to be done manually. 55 | -------------------------------------------------------------------------------- /410_Scaling/60_Index_per_user.md: -------------------------------------------------------------------------------- 1 | [[user-based]] 2 | === User-Based Data 3 | 4 | Often, users start using Elasticsearch because they need to add full-text 5 | search or analytics to an existing application.((("scaling", "user-based data")))((("user-based data"))) They create a single index 6 | that holds all of their documents. Gradually, others in the company realize 7 | how much benefit Elasticsearch brings, and they want to add their data to 8 | Elasticsearch as well. 9 | 10 | Fortunately, Elasticsearch supports 11 | http://en.wikipedia.org/wiki/Multitenancy[multitenancy] so each new user can 12 | have her own index in the same cluster.((("mulltitenancy"))) Occasionally, somebody will want to 13 | search across the documents for all users, which they can do by searching 14 | across all indices, but most of the time, users are interested in only their 15 | own documents. 16 | 17 | Some users have more documents than others, and some users will have heavier 18 | search loads than others, so the ability to specify the number of primary shards 19 | and replica shards that each index should have fits well with the index-per-user 20 | model.((("indices", "index-per-user model")))((("primary shards", "number per-index"))) Similarly, busier indices can be allocated to stronger boxes with shard 21 | allocation filtering. (See <>.) 22 | 23 | TIP: Don't just use the default number of primary shards for every index. 24 | Think about how much data that index needs to hold. It may be that all you 25 | need is one shard--any more is a waste of resources. 26 | 27 | Most users of Elasticsearch can stop here. A simple index-per-user approach 28 | is sufficient for the majority of cases. 29 | 30 | In exceptional cases, you may find that you need to support a large number of 31 | users, all with similar needs. An example might be hosting a search engine 32 | for thousands of email forums. ((("forums, resource allocation for"))) Some forums may have a huge amount of traffic, 33 | but the majority of forums are quite small. Dedicating an index with a single 34 | shard to a small forum is overkill--a single shard could hold the data for 35 | many forums. 36 | 37 | What we need is a way to share resources across users, to give the impression 38 | that each user has his own index without wasting resources on small users. 39 | 40 | -------------------------------------------------------------------------------- /410_Scaling/70_Faking_it.md: -------------------------------------------------------------------------------- 1 | [[faking-it]] 2 | === Faking Index per User with Aliases 3 | 4 | To keep our design simple and clean, we would((("scaling", "faking index-per-user with aliases")))((("aliases, index")))((("index aliases"))) like our application to believe that 5 | we have a dedicated index per user--or per forum in our example--even if 6 | the reality is that we are using one big <>. To do 7 | that, we need some way to hide the `routing` value and the filter on 8 | `forum_id`. 9 | 10 | Index aliases allow us to do just that. When you associate an alias with an 11 | index, you can also specify a filter and routing values: 12 | 13 | [source,json] 14 | ------------------------------ 15 | PUT /forums/_alias/baking 16 | { 17 | "routing": "baking", 18 | "filter": { 19 | "term": { 20 | "forum_id": "baking" 21 | } 22 | } 23 | } 24 | ------------------------------ 25 | 26 | Now, we can treat the `baking` alias as if it were its own index. Documents 27 | indexed into the `baking` alias automatically get the custom routing value 28 | applied: 29 | 30 | [source,json] 31 | ------------------------------ 32 | PUT /baking/post/1 <1> 33 | { 34 | "forum_id": "baking", <1> 35 | "title": "Easy recipe for ginger nuts", 36 | ... 37 | } 38 | ------------------------------ 39 | <1> We still need the `forum_id` field for the filter to work, but 40 | the custom routing value is now implicit. 41 | 42 | Queries run against the `baking` alias are run just on the shard associated 43 | with the custom routing value, and the results are automatically filtered by 44 | the filter we specified: 45 | 46 | [source,json] 47 | ------------------------------ 48 | GET /baking/post/_search 49 | { 50 | "query": { 51 | "match": { 52 | "title": "ginger nuts" 53 | } 54 | } 55 | } 56 | ------------------------------ 57 | 58 | Multiple aliases can be specified when searching across multiple forums: 59 | 60 | [source,json] 61 | ------------------------------ 62 | GET /baking,recipes/post/_search <1> 63 | { 64 | "query": { 65 | "match": { 66 | "title": "ginger nuts" 67 | } 68 | } 69 | } 70 | ------------------------------ 71 | <1> Both `routing` values are applied, and results can match either filter. 72 | 73 | -------------------------------------------------------------------------------- /410_Scaling/75_One_big_user.md: -------------------------------------------------------------------------------- 1 | [[one-big-user]] 2 | === One Big User 3 | 4 | Big, popular forums start out as small forums.((("forums, resource allocation for", "one big user"))) One day we will find that one 5 | shard in our shared index is doing a lot more work than the other shards, 6 | because it holds the documents for a forum that has become very popular. That 7 | forum now needs its own index. 8 | 9 | The index aliases that we're using to fake an index per user give us a clean 10 | migration path for the big forum.((("indices", "shared", "migrating data to dedicated index"))) 11 | 12 | The first step is to create a new index dedicated to the forum, and with the 13 | appropriate number of shards to allow for expected growth: 14 | 15 | [source,json] 16 | ------------------------------ 17 | PUT /baking_v1 18 | { 19 | "settings": { 20 | "number_of_shards": 3 21 | } 22 | } 23 | ------------------------------ 24 | 25 | The next step is to migrate the data from the shared index into the dedicated 26 | index, which can be done using <> and the 27 | <>. As soon as the migration is finished, the index alias 28 | can be updated to point to the new index: 29 | 30 | [source,json] 31 | ------------------------------ 32 | POST /_aliases 33 | { 34 | "actions": [ 35 | { "remove": { "alias": "baking", "index": "forums" }}, 36 | { "add": { "alias": "baking", "index": "baking_v1" }} 37 | ] 38 | } 39 | ------------------------------ 40 | 41 | Updating the alias is atomic; it's like throwing a switch. Your application 42 | continues talking to the `baking` API and is completely unaware that it now 43 | points to a new dedicated index. 44 | 45 | The dedicated index no longer needs the filter or the routing values. We can 46 | just rely on the default sharding that Elasticsearch does using each 47 | document's `_id` field. 48 | 49 | The last step is to remove the old documents from the shared index, which can 50 | be done with a `delete-by-query` request, using the original routing value and 51 | forum ID: 52 | 53 | [source,json] 54 | ------------------------------ 55 | DELETE /forums/post/_query?routing=baking 56 | { 57 | "query": { 58 | "term": { 59 | "forum_id": "baking" 60 | } 61 | } 62 | } 63 | ------------------------------ 64 | 65 | The beauty of this index-per-user model is that it allows you to reduce 66 | resources, keeping costs low, while still giving you the flexibility to scale 67 | out when necessary, and with zero downtime. 68 | -------------------------------------------------------------------------------- /500_Cluster_Admin/10_intro.md: -------------------------------------------------------------------------------- 1 | 2 | Elasticsearch is often deployed as a cluster of nodes.((("clusters", "administration"))) A variety of 3 | APIs let you manage and monitor the cluster itself, rather than interact 4 | with the data stored within the cluster. 5 | 6 | As with most functionality in Elasticsearch, there is an overarching design goal 7 | that tasks should be performed through an API rather than by modifying static 8 | configuration files. This becomes especially important as your cluster scales. 9 | Even with a provisioning system (such as Puppet, Chef, and Ansible), a single HTTP API call 10 | is often simpler than pushing new configurations to hundreds of physical machines. 11 | 12 | To that end, this chapter presents the various APIs that allow you to 13 | dynamically tweak, tune, and configure your cluster. It also covers a 14 | host of APIs that provide statistics about the cluster itself so you can 15 | monitor for health and performance. 16 | -------------------------------------------------------------------------------- /500_Cluster_Admin/15_marvel.md: -------------------------------------------------------------------------------- 1 | 2 | === Marvel for Monitoring 3 | 4 | At the very beginning of the book (<>), we encouraged you to install 5 | Marvel,((("Marvel", "monitoring with")))((("clusters", "administration", "Marvel for monitoring"))) a management monitoring tool for Elasticsearch, because it would enable 6 | interactive code samples throughout the book. 7 | 8 | If you didn't install Marvel then, we encourage you to install it now. This 9 | chapter introduces a large number of APIs that emit an even larger number 10 | of statistics. These stats track everything from heap memory usage and garbage 11 | collection counts to open file descriptors. These statistics are invaluable 12 | for debugging a misbehaving cluster. 13 | 14 | The problem is that these APIs provide a single data point: the statistic 15 | _right now_. Often you'll want to see historical data too, so you can 16 | plot a trend. Knowing memory usage at this instant is helpful, but knowing 17 | memory usage _over time_ is much more useful. 18 | 19 | Furthermore, the output of these APIs can get truly hairy as your cluster grows. 20 | Once you have a dozen nodes, let alone a hundred, reading through stacks of JSON 21 | becomes very tedious. 22 | 23 | Marvel periodically polls these APIs and stores the data back in Elasticsearch. 24 | This allows Marvel to query and aggregate the metrics, and then provide interactive 25 | graphs in your browser. There are no proprietary statistics that Marvel exposes; 26 | it uses the same stats APIs that are accessible to you. But it does greatly 27 | simplify the collection and graphing of those statistics. 28 | 29 | Marvel is free to use in development, so you should definitely try it out! 30 | -------------------------------------------------------------------------------- /510_Deployment/10_intro.md: -------------------------------------------------------------------------------- 1 | If you have made it this far in the book, hopefully you've learned a thing or 2 | two about Elasticsearch and are ready to((("deployment"))) deploy your cluster to production.((("clusters", "deployment", see="deployment"))) 3 | This chapter is not meant to be an exhaustive guide to running your cluster 4 | in production, but it covers the key things to consider before putting 5 | your cluster live. 6 | 7 | Three main areas are covered: 8 | 9 | - Logistical considerations, such as hardware recommendations and deployment 10 | strategies 11 | - Configuration changes that are more suited to a production environment 12 | - Post-deployment considerations, such as security, maximizing indexing performance, 13 | and backups 14 | 15 | -------------------------------------------------------------------------------- /510_Deployment/60_file_descriptors.md: -------------------------------------------------------------------------------- 1 | 2 | === File Descriptors and MMap 3 | 4 | Lucene uses a _very_ large number of files. ((("deployment", "file descriptors and MMap"))) At the same time, Elasticsearch 5 | uses a large number of sockets to communicate between nodes and HTTP clients. 6 | All of this requires available file descriptors.((("file descriptors"))) 7 | 8 | Sadly, many modern Linux distributions ship with a paltry 1,024 file descriptors 9 | allowed per process. This is _far_ too low for even a small Elasticsearch 10 | node, let alone one that is handling hundreds of indices. 11 | 12 | You should increase your file descriptor count to something very large, such as 13 | 64,000. This process is irritatingly difficult and highly dependent on your 14 | particular OS and distribution. Consult the documentation for your OS to determine 15 | how best to change the allowed file descriptor count. 16 | 17 | Once you think you've changed it, check Elasticsearch to make sure it really does 18 | have enough file descriptors: 19 | 20 | [source,js] 21 | ---- 22 | GET /_nodes/process 23 | 24 | { 25 | "cluster_name": "elasticsearch__zach", 26 | "nodes": { 27 | "TGn9iO2_QQKb0kavcLbnDw": { 28 | "name": "Zach", 29 | "transport_address": "inet[/192.168.1.131:9300]", 30 | "host": "zacharys-air", 31 | "ip": "192.168.1.131", 32 | "version": "2.0.0-SNAPSHOT", 33 | "build": "612f461", 34 | "http_address": "inet[/192.168.1.131:9200]", 35 | "process": { 36 | "refresh_interval_in_millis": 1000, 37 | "id": 19808, 38 | "max_file_descriptors": 64000, <1> 39 | "mlockall": true 40 | } 41 | } 42 | } 43 | } 44 | ---- 45 | <1> The `max_file_descriptors` field shows the number of available descriptors that 46 | the Elasticsearch process can access. 47 | 48 | Elasticsearch also uses a mix of NioFS and MMapFS ((("MMapFS")))for the various files. Ensure 49 | that you configure the maximum map count so that there is ample virtual memory available for 50 | mmapped files. This can be set temporarily: 51 | 52 | [source,js] 53 | ---- 54 | sysctl -w vm.max_map_count=262144 55 | ---- 56 | 57 | Or you can set it permanently by modifying `vm.max_map_count` setting in your `/etc/sysctl.conf`. 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /510_Deployment/70_conclusion.md: -------------------------------------------------------------------------------- 1 | 2 | === Revisit This List Before Production 3 | 4 | You are likely reading this section before you go into production. 5 | The details covered in this chapter are good to be generally aware of, but it is 6 | critical to revisit this entire list right before deploying to production. 7 | 8 | Some of the topics will simply stop you cold (such as too few available file 9 | descriptors). These are easy enough to debug because they are quickly apparent. 10 | Other issues, such as split brains and memory settings, are visible only after 11 | something bad happens. At that point, the resolution is often messy and tedious. 12 | 13 | It is much better to proactively prevent these situations from occurring by configuring 14 | your cluster appropriately _before_ disaster strikes. So if you are going to 15 | dog-ear (or bookmark) one section from the entire book, this chapter would be 16 | a good candidate. The week before deploying to production, simply flip through 17 | the list presented here and check off all the recommendations. 18 | -------------------------------------------------------------------------------- /510_Deployment/80_cluster_settings.md: -------------------------------------------------------------------------------- 1 | 2 | === -------------------------------------------------------------------------------- /520_Post_Deployment/00_Intro.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/520_Post_Deployment/00_Intro.md -------------------------------------------------------------------------------- /520_Post_Deployment/10_dynamic_settings.md: -------------------------------------------------------------------------------- 1 | 2 | === Changing Settings Dynamically 3 | 4 | Many settings in Elasticsearch are dynamic and can be modified through the API. 5 | Configuration changes that force a node (or cluster) restart are strenuously avoided.((("post-deployment", "changing settings dynamically"))) 6 | And while it's possible to make the changes through the static configs, we 7 | recommend that you use the API instead. 8 | 9 | The `cluster-update` API operates((("Cluster Update API"))) in two modes: 10 | 11 | Transient:: 12 | These changes are in effect until the cluster restarts. Once 13 | a full cluster restart takes place, these settings are erased. 14 | 15 | Persistent:: 16 | These changes are permanently in place unless explicitly changed. 17 | They will survive full cluster restarts and override the static configuration files. 18 | 19 | Transient versus persistent settings are supplied in the JSON body: 20 | 21 | [source,js] 22 | ---- 23 | PUT /_cluster/settings 24 | { 25 | "persistent" : { 26 | "discovery.zen.minimum_master_nodes" : 2 <1> 27 | }, 28 | "transient" : { 29 | "indices.store.throttle.max_bytes_per_sec" : "50mb" <2> 30 | } 31 | } 32 | ---- 33 | <1> This persistent setting will survive full cluster restarts. 34 | <2> This transient setting will be removed after the first full cluster 35 | restart. 36 | 37 | A complete list of settings that can be updated dynamically can be found in the 38 | http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-update-settings.html[online reference docs]. 39 | 40 | -------------------------------------------------------------------------------- /520_Post_Deployment/70_conclusion.md: -------------------------------------------------------------------------------- 1 | 2 | === Clusters Are Living, Breathing Creatures 3 | 4 | Once you get a cluster into production, you'll find that it takes on a life of its 5 | own. ((("clusters", "maintaining")))((("post-deployment", "clusters, rolling restarts and upgrades")))Elasticsearch works hard to make clusters self-sufficient and _just work_. 6 | But a cluster still requires routine care and feeding, such as routine backups 7 | and upgrades. 8 | 9 | Elasticsearch releases new versions with bug fixes and performance enhancements at 10 | a very fast pace, and it is always a good idea to keep your cluster current. 11 | Similarly, Lucene continues to find new and exciting bugs in the JVM itself, which 12 | means you should always try to keep your JVM up-to-date. 13 | 14 | This means it is a good idea to have a standardized, routine way to perform rolling 15 | restarts and upgrades in your cluster. Upgrading should be a routine process, 16 | rather than a once-yearly fiasco that requires countless hours of precise planning. 17 | 18 | Similarly, it is important to have disaster recovery plans in place. Take frequent 19 | snapshots of your cluster--and periodically _test_ those snapshots by performing 20 | a real recovery! It is all too common for organizations to make routine backups but 21 | never test their recovery strategy. Often you'll find a glaring deficiency 22 | the first time you perform a real recovery (such as users being unaware of which 23 | drive to mount). It's better to work these bugs out of your process with 24 | routine testing, rather than at 3 a.m. when there is a crisis. 25 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # 版本变动 2 | 2015-04-25 3 | 1. 060_Distributed_Search 4 | 5 | 2015-05-04 6 | 1. 310_Geopoints 7 | 8 | 2015-05-12 9 | 1. 310_Geopoints 10 | 11 | 2015-05-15 12 | 1. 310_Geopoints 13 | 14 | 2015-05-17 15 | 1. 330_Geo_aggs 16 | 17 | 2015-05-17 18 | Fix 19 | 1. 020_Distributed_Cluster/05_Empty_cluster.md 20 | 2. 020_Distributed_Cluster/10_Cluster_health.md 21 | 22 | 2015-06-02 23 | 1. 340_Geoshapes 24 | 25 | 2015-10-13 26 | 1. 索引英文内容 27 | 2. 将Geo部分索引 28 | 29 | 2015-10-22 30 | 402/00~33 翻译完成 by LukeHong -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Elasticsearch 权威指南(中文版)", 3 | "introduction": "Elasticsearch 权威指南的中文译本。", 4 | "path": { 5 | "toc": "TOC.md", 6 | "readme": "README_Wiki.md" 7 | } 8 | } -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/cover.jpg -------------------------------------------------------------------------------- /cover.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/cover.psd -------------------------------------------------------------------------------- /cover/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/cover/background.jpg -------------------------------------------------------------------------------- /cover/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/cover/logo.png -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/cover_small.jpg -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/cover.png -------------------------------------------------------------------------------- /images/elas_0201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0201.png -------------------------------------------------------------------------------- /images/elas_0202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0202.png -------------------------------------------------------------------------------- /images/elas_0203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0203.png -------------------------------------------------------------------------------- /images/elas_0204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0204.png -------------------------------------------------------------------------------- /images/elas_0205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0205.png -------------------------------------------------------------------------------- /images/elas_0206.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0206.png -------------------------------------------------------------------------------- /images/elas_0301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0301.png -------------------------------------------------------------------------------- /images/elas_0401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0401.png -------------------------------------------------------------------------------- /images/elas_0402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0402.png -------------------------------------------------------------------------------- /images/elas_0403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0403.png -------------------------------------------------------------------------------- /images/elas_0404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0404.png -------------------------------------------------------------------------------- /images/elas_0405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0405.png -------------------------------------------------------------------------------- /images/elas_0406.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0406.png -------------------------------------------------------------------------------- /images/elas_0901.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0901.png -------------------------------------------------------------------------------- /images/elas_0902.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_0902.png -------------------------------------------------------------------------------- /images/elas_1101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1101.png -------------------------------------------------------------------------------- /images/elas_1102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1102.png -------------------------------------------------------------------------------- /images/elas_1103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1103.png -------------------------------------------------------------------------------- /images/elas_1104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1104.png -------------------------------------------------------------------------------- /images/elas_1105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1105.png -------------------------------------------------------------------------------- /images/elas_1106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1106.png -------------------------------------------------------------------------------- /images/elas_1107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1107.png -------------------------------------------------------------------------------- /images/elas_1108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1108.png -------------------------------------------------------------------------------- /images/elas_1109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1109.png -------------------------------------------------------------------------------- /images/elas_1110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1110.png -------------------------------------------------------------------------------- /images/elas_1111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1111.png -------------------------------------------------------------------------------- /images/elas_1701.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1701.png -------------------------------------------------------------------------------- /images/elas_1702.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1702.png -------------------------------------------------------------------------------- /images/elas_1703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1703.png -------------------------------------------------------------------------------- /images/elas_1704.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1704.png -------------------------------------------------------------------------------- /images/elas_1705.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1705.png -------------------------------------------------------------------------------- /images/elas_1706.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_1706.png -------------------------------------------------------------------------------- /images/elas_17in01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_17in01.png -------------------------------------------------------------------------------- /images/elas_17in02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_17in02.png -------------------------------------------------------------------------------- /images/elas_28in01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_28in01.png -------------------------------------------------------------------------------- /images/elas_28in02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_28in02.png -------------------------------------------------------------------------------- /images/elas_29in01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_29in01.png -------------------------------------------------------------------------------- /images/elas_29in02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_29in02.png -------------------------------------------------------------------------------- /images/elas_29in03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_29in03.png -------------------------------------------------------------------------------- /images/elas_33in01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_33in01.png -------------------------------------------------------------------------------- /images/elas_33in02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_33in02.png -------------------------------------------------------------------------------- /images/elas_4401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_4401.png -------------------------------------------------------------------------------- /images/elas_4402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_4402.png -------------------------------------------------------------------------------- /images/elas_4403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_4403.png -------------------------------------------------------------------------------- /images/elas_4404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/looly/elasticsearch-definitive-guide-cn/ef70babe5fdd248f9fa092c7671e8d4e4b6b8ce5/images/elas_4404.png -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Push to origin master' 4 | git push origin master 5 | echo 'Push to osc master' 6 | git push osc master 7 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # pull 4 | git pull origin master 5 | 6 | ./push.sh --------------------------------------------------------------------------------