├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── elasticsearch │ │ └── mapper │ │ ├── annotations │ │ ├── Document.java │ │ ├── IgnoreField.java │ │ ├── enums │ │ │ ├── IndexOptions.java │ │ │ ├── NumberType.java │ │ │ ├── RangeType.java │ │ │ ├── SimilarityAlgorithm.java │ │ │ ├── StringType.java │ │ │ └── TermVector.java │ │ ├── fieldtype │ │ │ ├── BinaryField.java │ │ │ ├── BooleanField.java │ │ │ ├── CompletionContext.java │ │ │ ├── CompletionField.java │ │ │ ├── DateField.java │ │ │ ├── Fielddata.java │ │ │ ├── FielddataFrequencyFilter.java │ │ │ ├── GeoPointField.java │ │ │ ├── IPField.java │ │ │ ├── MultiField.java │ │ │ ├── MultiNestedField.java │ │ │ ├── NumberField.java │ │ │ ├── PercolatorField.java │ │ │ ├── RangeField.java │ │ │ ├── StringField.java │ │ │ └── TokenCountField.java │ │ └── meta │ │ │ ├── MetaField_All.java │ │ │ ├── MetaField_Parent.java │ │ │ ├── MetaField_Routing.java │ │ │ └── MetaField_Source.java │ │ ├── bean │ │ ├── GeoPoint.java │ │ └── Range.java │ │ ├── mapper │ │ ├── BinaryFieldMapper.java │ │ ├── BooleanFieldMapper.java │ │ ├── CompletionFieldMapper.java │ │ ├── DateFieldMapper.java │ │ ├── GeoPointFieldMapper.java │ │ ├── IPFieldMapper.java │ │ ├── MappingBuilder.java │ │ ├── MultiFieldMapper.java │ │ ├── NumericFieldMapper.java │ │ ├── PercolatorFieldMapper.java │ │ ├── RangeFieldMapper.java │ │ ├── StringFieldMapper.java │ │ └── TokenCountFieldMapper.java │ │ └── utils │ │ ├── BeanUtils.java │ │ └── StringUtils.java └── resources │ └── log4j2.properties └── test └── java └── org └── elasticsearch └── mapper ├── entity ├── Component.java ├── Computer.java ├── Cpu.java ├── MacBook.java ├── Memory.java └── User.java └── test └── MappingTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | *.ipr 9 | *.iws 10 | *.iml 11 | .classpath 12 | .gradle* 13 | build/* 14 | target/* 15 | out 16 | .idea 17 | draft.story 18 | chromedriver.log 19 | .classpath 20 | .gitignore.swp 21 | .project 22 | .settings/ 23 | *.ids 24 | gradle.properties 25 | *.log 26 | *.log.* 27 | *.bak 28 | cango* 29 | bin/ 30 | .download/* 31 | logs/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | org.elasticsearch.mapper.x2.annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "{}" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright 2016 music_code_m 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #elasticsearch-mapper 2 | ```java 3 | org.elasticsearch.mapper.test.MappingTest 4 | 5 | 6 | @Document(_type = "macBook", _all = @MetaField_All(enabled = false), _parent = @MetaField_Parent(parentClass = Computer.class)) 7 | public class MacBook extends Component { 8 | 9 | // default: keyword 10 | private String deviceName; 11 | 12 | @StringField(type = StringType.Text) 13 | private String manufacturer; 14 | 15 | @MultiField( 16 | mainField = @StringField(type = StringType.Keyword, boost = 2.0f), 17 | fields = { 18 | @MultiNestedField(name = "pinyin", field = @StringField(type = StringType.Text, analyzer = "lc_pinyin")), 19 | @MultiNestedField(name = "cn", field = @StringField(type = StringType.Text, analyzer = "ik_smart")), 20 | @MultiNestedField(name = "en", field = @StringField(type = StringType.Text, analyzer = "english")), 21 | }, 22 | tokenFields = { 23 | @TokenCountField(name = "cnTokenCount", analyzer = "ik_smart") 24 | } 25 | ) 26 | private String introduction; 27 | 28 | // nested doc 29 | private List users; 30 | 31 | // inner doc 32 | private Cpu cpu; 33 | 34 | //inner doc 35 | private Memory memory; 36 | } 37 | 38 | public class Component { 39 | 40 | private String serialNo; 41 | 42 | private String madeIn; 43 | } 44 | 45 | @Document(_type = "computer") 46 | public class Computer { 47 | private String parentField; 48 | } 49 | 50 | 51 | public class User { 52 | 53 | private boolean isRoot; 54 | 55 | private String username; 56 | 57 | private String password; 58 | } 59 | 60 | public class Cpu extends Component { 61 | private int coreNumber; 62 | } 63 | 64 | 65 | public class Memory extends Component { 66 | @NumberField(type = NumberType.Byte) 67 | private Byte memorySize; 68 | } 69 | 70 | { 71 | "computer" : { 72 | "properties" : { 73 | "parentField" : { 74 | "type" : "keyword" 75 | } 76 | } 77 | } 78 | } 79 | 80 | 81 | { 82 | "macBook" : { 83 | "_all" : { 84 | "enabled" : false 85 | }, 86 | "_parent" : { 87 | "type" : "computer" 88 | }, 89 | "properties" : { 90 | "deviceName" : { 91 | "type" : "keyword" 92 | }, 93 | "manufacturer" : { 94 | "type" : "text" 95 | }, 96 | "introduction" : { 97 | "type" : "keyword", 98 | "boost" : 2.0, 99 | "fields" : { 100 | "pinyin" : { 101 | "type" : "text", 102 | "analyzer" : "lc_pinyin" 103 | }, 104 | "cn" : { 105 | "type" : "text", 106 | "analyzer" : "ik_smart" 107 | }, 108 | "en" : { 109 | "type" : "text", 110 | "analyzer" : "english" 111 | }, 112 | "cnTokenCount" : { 113 | "type" : "token_count", 114 | "analyzer" : "ik_smart" 115 | } 116 | } 117 | }, 118 | "users" : { 119 | "type" : "nested", 120 | "properties" : { 121 | "isRoot" : { 122 | "type" : "boolean" 123 | }, 124 | "username" : { 125 | "type" : "keyword" 126 | }, 127 | "password" : { 128 | "type" : "keyword" 129 | } 130 | } 131 | }, 132 | "cpu" : { 133 | "type" : "object", 134 | "properties" : { 135 | "coreNumber" : { 136 | "type" : "integer" 137 | }, 138 | "serialNo" : { 139 | "type" : "keyword" 140 | }, 141 | "madeIn" : { 142 | "type" : "keyword" 143 | } 144 | } 145 | }, 146 | "memory" : { 147 | "type" : "object", 148 | "properties" : { 149 | "memorySize" : { 150 | "type" : "byte" 151 | }, 152 | "serialNo" : { 153 | "type" : "keyword" 154 | }, 155 | "madeIn" : { 156 | "type" : "keyword" 157 | } 158 | } 159 | }, 160 | "serialNo" : { 161 | "type" : "keyword" 162 | }, 163 | "madeIn" : { 164 | "type" : "keyword" 165 | } 166 | } 167 | } 168 | } 169 | 170 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | elasticsearch-mapper 6 | 4.0.0 7 | org.elasticsearch 8 | elasticsearch-mapper 9 | 5.2.0 10 | jar 11 | ElasticSearch document mapping 12 | 2017 13 | 14 | 15 | The Apache Software License, Version 2.0 16 | http://www.apache.org/licenses/LICENSE-2.0.txt 17 | repo 18 | 19 | 20 | 21 | 22 | 5.2.0 23 | 1.8 24 | 25 | 26 | 27 | 28 | uk.maven.org 29 | UK MAVEN 30 | 31 | true 32 | 33 | 34 | true 35 | 36 | http://uk.maven.org/maven2/ 37 | 38 | 39 | 40 | 41 | 42 | org.elasticsearch.client 43 | transport 44 | ${elasticsearch.version} 45 | 46 | 47 | 48 | org.apache.logging.log4j 49 | log4j-api 50 | 2.7 51 | 52 | 53 | org.apache.logging.log4j 54 | log4j-core 55 | 2.7 56 | 57 | 58 | 59 | com.google.guava 60 | guava 61 | 18.0 62 | 63 | 64 | 65 | 66 | junit 67 | junit 68 | 4.10 69 | test 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | 2.3.2 79 | 80 | utf-8 81 | 1.8 82 | 1.8 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-surefire-plugin 88 | 2.11 89 | 90 | 91 | **/*Tests.java 92 | 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-source-plugin 98 | 2.1.2 99 | 100 | 101 | attach-sources 102 | 103 | jar 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/Document.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations; 2 | 3 | import org.elasticsearch.mapper.annotations.meta.MetaField_All; 4 | import org.elasticsearch.mapper.annotations.meta.MetaField_Routing; 5 | import org.elasticsearch.mapper.annotations.meta.MetaField_Source; 6 | import org.elasticsearch.mapper.annotations.meta.MetaField_Parent; 7 | 8 | import java.lang.annotation.*; 9 | 10 | @Inherited 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target({ElementType.TYPE}) 13 | public @interface Document { 14 | 15 | /** 16 | * By default, fields can be added dynamically to a document, 17 | * or to inner objects within a document, 18 | * just by indexing a document containing the new field. 19 | */ 20 | boolean dynamic() default true; 21 | 22 | /** 23 | * the type of document 24 | */ 25 | String _type(); 26 | 27 | /** 28 | * The _all field is a special catch-all field which concatenates 29 | * the values of all of the other fields into one big string, 30 | * using space as a delimiter, which is then analyzed and indexed, 31 | * but not stored. This means that it can be searched, but not retrieved 32 | */ 33 | MetaField_All _all() default @MetaField_All(enabled = true, store = false); 34 | 35 | /** 36 | * parent type 37 | */ 38 | MetaField_Parent _parent() default @MetaField_Parent(parentClass = {}); 39 | 40 | /** 41 | * A document is routed to a particular shard in an index using the following formula: 42 | *
shard_num = hash(_routing) % num_primary_shards
43 | *

44 | * Forgetting the routing value can lead to a document being indexed on more than one shard. 45 | * As a safeguard, the _routing field can be configured to make a custom routing value required for all CRUD operations 46 | */ 47 | MetaField_Routing _routing() default @MetaField_Routing(required = false); 48 | 49 | 50 | /** 51 | * The _source field contains the original JSON document body that was passed at index time. 52 | * The _source field itself is not indexed (and thus is not searchable), 53 | * but it is stored so that it can be returned when executing fetch requests, like get or search 54 | */ 55 | MetaField_Source _source() default @MetaField_Source(enabled = true); 56 | 57 | /** 58 | * Each mapping type can have custom meta data associated with it. 59 | * These are not used at all by Elasticsearch, but can be used to store application-specific metadata, 60 | * such as the class that a document belongs to: 61 | *

62 |      * PUT my_index
63 |      * {
64 |      *   "mappings": {
65 |      *     "user": {
66 |      *       "_meta": {
67 |      *         "class": "MyApp::User",
68 |      *         "version": {
69 |      *           "min": "1.0",
70 |      *           "max": "1.3"
71 |      *         }
72 |      *       }
73 |      *     }
74 |      *   }
75 |      * }
76 |      * 
77 | *

78 | * https://www.elastic.co/guide/en/elasticsearch/reference/5.2/mapping-meta-field.html 79 | */ 80 | String _meta() default ""; 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/IgnoreField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface IgnoreField { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/IndexOptions.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | public enum IndexOptions { 4 | Default { 5 | @Override 6 | public String code() { 7 | return "none"; 8 | } 9 | }, 10 | 11 | /** 12 | * Only the doc number is indexed. Can answer the question Does this term exist in this field? 13 | */ 14 | Docs { 15 | @Override 16 | public String code() { 17 | return "docs"; 18 | } 19 | }, 20 | 21 | /** 22 | * Doc number and term frequencies are indexed. 23 | * Term frequencies are used to score repeated terms higher than single terms. 24 | */ 25 | Freqs { 26 | @Override 27 | public String code() { 28 | return "freqs"; 29 | } 30 | }, 31 | 32 | /** 33 | * Doc number, term frequencies, and term positions (or order) are indexed. 34 | * Positions can be used for proximity or phrase queries. 35 | */ 36 | Positions { 37 | @Override 38 | public String code() { 39 | return "positions"; 40 | } 41 | }, 42 | 43 | /** 44 | * Doc number, term frequencies, positions, 45 | * and start and end character offsets (which map the term back to the original string) are indexed. 46 | * Offsets are used by the postings highlighter. 47 | */ 48 | Offsets { 49 | @Override 50 | public String code() { 51 | return "offsets"; 52 | } 53 | }; 54 | 55 | public abstract String code(); 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/NumberType.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | import java.lang.String; 4 | 5 | /** 6 | * As far as integer types (byte, short, integer and long) are concerned, 7 | * you should pick the smallest type which is enough for your use-case. 8 | * This will help indexing and searching be more efficient. Note however that given that 9 | * storage is optimized based on the actual values that are stored, picking one type over 10 | * another one will have no impact on storage requirements. 11 | *

12 | * For floating-point types, it is often more efficient to store floating-point data into an integer using a scaling factor, 13 | * which is what the scaled_float type does under the hood. For instance, a price field could be 14 | * stored in a scaled_float with a scaling_factor of 100. All APIs would work as if the field was stored as a double, 15 | * but under the hood elasticsearch would be working with the number of cents, price*100, which is an integer. 16 | * This is mostly helpful to save disk space since integers are way easier to compress than floating points. 17 | * scaled_float is also fine to use in order to trade accuracy for disk space. 18 | * For instance imagine that you are tracking cpu utilization as a number between 0 and 1. 19 | * It usually does not matter much whether cpu utilization is 12.7% or 13%, 20 | * so you could use a scaled_float with a scaling_factor of 100 in order to round cpu utilization to the closest percent in order to save space. 21 | *

22 | * If scaled_float is not a good fit, then you should pick the smallest type that is enough for the use-case 23 | * among the floating-point types: double, float and half_float. Here is a table that compares these types in order to help make a decision. 24 | * 25 | * @author chennan 26 | */ 27 | public enum NumberType { 28 | /** 29 | * A signed 64-bit integer with a minimum value of -2^63 and a maximum value of 2^63-1. 30 | */ 31 | Long { 32 | @Override 33 | public String code() { 34 | return "long"; 35 | } 36 | }, 37 | 38 | /** 39 | * A signed 32-bit integer with a minimum value of -2^31 and a maximum value of 2^31-1. 40 | */ 41 | Integer { 42 | @Override 43 | public String code() { 44 | return "integer"; 45 | } 46 | }, 47 | 48 | /** 49 | * A signed 16-bit integer with a minimum value of -32,768 and a maximum value of 32,767. 50 | */ 51 | Short { 52 | @Override 53 | public String code() { 54 | return "short"; 55 | } 56 | }, 57 | 58 | /** 59 | * A signed 8-bit integer with a minimum value of -128 and a maximum value of 127. 60 | */ 61 | Byte { 62 | @Override 63 | public String code() { 64 | return "byte"; 65 | } 66 | }, 67 | 68 | /** 69 | * A double-precision 64-bit IEEE 754 floating point 70 | */ 71 | Double { 72 | @Override 73 | public String code() { 74 | return "double"; 75 | } 76 | }, 77 | 78 | /** 79 | * A single-precision 32-bit IEEE 754 floating point. 80 | */ 81 | Float { 82 | @Override 83 | public String code() { 84 | return "float"; 85 | } 86 | }, 87 | 88 | /** 89 | * A half-precision 16-bit IEEE 754 floating point. 90 | */ 91 | HalfFloat { 92 | @Override 93 | public String code() { 94 | return "half_float"; 95 | } 96 | }, 97 | 98 | /** 99 | * A floating point that is backed by a long and a fixed scaling factor. 100 | */ 101 | ScaledFloat { 102 | @Override 103 | public String code() { 104 | return "scaled_float"; 105 | } 106 | }; 107 | 108 | public abstract java.lang.String code(); 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/RangeType.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | public enum RangeType { 4 | /** 5 | * A range of signed 32-bit integers with a minimum value of -2^31 and maximum of 2^31-1. 6 | */ 7 | IntegerRange { 8 | @Override 9 | public String code() { 10 | return "integer_range"; 11 | } 12 | }, 13 | 14 | /** 15 | * A range of single-precision 32-bit IEEE 754 floating point values. 16 | */ 17 | FloatRange { 18 | @Override 19 | public String code() { 20 | return "float_range"; 21 | } 22 | }, 23 | 24 | /** 25 | * A range of signed 64-bit integers with a minimum value of -263 and maximum of 263-1. 26 | */ 27 | LongRange { 28 | @Override 29 | public String code() { 30 | return "long_range"; 31 | } 32 | }, 33 | 34 | /** 35 | * A range of double-precision 64-bit IEEE 754 floating point values. 36 | */ 37 | DoubleRange { 38 | @Override 39 | public String code() { 40 | return "double_range"; 41 | } 42 | }, 43 | 44 | /** 45 | * A range of date values represented as unsigned 64-bit integer milliseconds elapsed since system epoch. 46 | */ 47 | DateRange { 48 | @Override 49 | public String code() { 50 | return "date_range"; 51 | } 52 | }; 53 | 54 | public abstract java.lang.String code(); 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/SimilarityAlgorithm.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | import java.lang.String; 4 | 5 | public enum SimilarityAlgorithm { 6 | Default { 7 | @Override 8 | public String code() { 9 | return "BM25"; 10 | } 11 | }, 12 | BM25 { 13 | @Override 14 | public String code() { 15 | return "BM25"; 16 | } 17 | }; 18 | 19 | public abstract java.lang.String code(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/StringType.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | import java.lang.String; 4 | 5 | public enum StringType { 6 | /** 7 | * A field to index structured content such as email addresses, hostnames, status codes, zip codes or tags. 8 | *

9 | * They are typically used for filtering (Find me all blog posts where status is published), 10 | * for sorting, and for aggregations. Keyword fields are only searchable by their exact value. 11 | */ 12 | Keyword { 13 | @Override 14 | public String code() { 15 | return "keyword"; 16 | } 17 | }, 18 | 19 | /** 20 | * A field to index full-text values, such as the body of an email or the description of a product. 21 | * These fields are analyzed, that is they are passed through an analyzer to convert the string into 22 | * a list of individual terms before being indexed. 23 | * The analysis process allows Elasticsearch to search for individual words within each full text field. 24 | * Text fields are not used for sorting and seldom used for aggregations 25 | */ 26 | Text { 27 | @Override 28 | public String code() { 29 | return "text"; 30 | } 31 | }; 32 | 33 | public abstract java.lang.String code(); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/enums/TermVector.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.enums; 2 | 3 | 4 | public enum TermVector { 5 | /** 6 | * No term vectors are stored. (default) 7 | */ 8 | No { 9 | @Override 10 | public String code() { 11 | return "code"; 12 | } 13 | }, 14 | 15 | /** 16 | * Just the terms in the field are stored. 17 | */ 18 | Yes { 19 | @Override 20 | public String code() { 21 | return "yes"; 22 | } 23 | }, 24 | 25 | /** 26 | * Terms and positions are stored. 27 | */ 28 | WithPositions { 29 | @Override 30 | public String code() { 31 | return "with_positions"; 32 | } 33 | }, 34 | /** 35 | * Terms and character offsets are stored. 36 | */ 37 | WithOffsets { 38 | @Override 39 | public String code() { 40 | return "with_offsets"; 41 | } 42 | }, 43 | 44 | /** 45 | * Terms, positions, and character offsets are stored. 46 | */ 47 | WithPositionsOffsets { 48 | @Override 49 | public String code() { 50 | return "with_positions_offsets"; 51 | } 52 | }; 53 | 54 | public abstract String code(); 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/BinaryField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface BinaryField { 9 | 10 | /** 11 | * Should the field be stored on disk in a column-stride fashion, 12 | * so that it can later be used for sorting, aggregations, or scripting? 13 | * Accepts true (default) or false. 14 | */ 15 | boolean doc_values() default true; 16 | 17 | /** 18 | * Whether the field value should be stored and retrievable separately from the _source field. 19 | * Accepts true or false (default). 20 | */ 21 | boolean store() default false; 22 | } -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/BooleanField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface BooleanField { 9 | /** 10 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 11 | */ 12 | float boost() default 1.0f; 13 | 14 | /** 15 | * Should the field be stored on disk in a column-stride fashion, 16 | * so that it can later be used for sorting, aggregations, or scripting? 17 | * Accepts true (default) or false. 18 | */ 19 | boolean doc_values() default true; 20 | 21 | /** 22 | * Should the field be searchable? Accepts true (default) or false. 23 | */ 24 | boolean index() default true; 25 | 26 | /** 27 | * Accepts any of the true or false values listed above. The value is substituted for any explicit null values. 28 | * Defaults to null, which means the field is treated as missing. 29 | */ 30 | String null_value() default ""; 31 | 32 | /** 33 | * Whether the field value should be stored and retrievable separately from the _source field. 34 | * Accepts true or false (default). 35 | */ 36 | boolean store() default false; 37 | } -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/CompletionContext.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | public @interface CompletionContext { 4 | 5 | String name(); 6 | 7 | String type() default "category"; 8 | 9 | String path() default ""; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/CompletionField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.FIELD}) 7 | @Documented 8 | @Inherited 9 | public @interface CompletionField { 10 | 11 | /** 12 | * The index analyzer to use, defaults to simple. In case you are wondering why we did 13 | * not opt for the standard analyzer: We try to have easy to understand behaviour here, 14 | * and if you index the field content At the Drive-in, 15 | * you will not get any suggestions for a, nor for d (the first non stopword). 16 | */ 17 | String analyzer() default "simple"; 18 | 19 | /** 20 | * The search analyzer to use, defaults to value of analyzer. 21 | */ 22 | String search_analyzer() default "simple"; 23 | 24 | /** 25 | * Preserves the separators, defaults to true. 26 | * If disabled, you could find a field starting with Foo Fighters,if you suggest for foof. 27 | */ 28 | boolean preserve_separators() default true; 29 | 30 | /** 31 | * Enables position increments, defaults to true. If disabled and using stopwords analyzer, 32 | * you could get a field starting with The Beatles, 33 | * if you suggest for b. Note: You could also achieve this by indexing two inputs, 34 | * Beatles and The Beatles, no need to change a simple analyzer, if you are able to enrich your data. 35 | */ 36 | boolean preserve_position_increments() default true; 37 | 38 | /** 39 | * Limits the length of a single input, defaults to 50 UTF-16 code points. 40 | * This limit is only used at index time to reduce the total number of characters per input string 41 | * in order to prevent massive inputs from bloating the underlying datastructure. 42 | * Most usecases won’t be influenced by the default value since prefix completions seldom grow beyond 43 | * prefixes longer than a handful of characters. 44 | */ 45 | int max_input_length() default 50; 46 | 47 | /** 48 | * To achieve suggestion filtering and/or boosting, you can add context mappings while configuring a completion field. 49 | * You can define multiple context mappings for a completion field. Every context mapping has a unique name and a type. 50 | * There are two types: category and geo. 51 | * Context mappings are configured under the contexts parameter in the field mapping. 52 | */ 53 | CompletionContext[] contexts() default {}; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/DateField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface DateField { 9 | 10 | /** 11 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 12 | */ 13 | float boost() default 1.0f; 14 | 15 | /** 16 | * Should the field be stored on disk in a column-stride fashion, 17 | * so that it can later be used for sorting, aggregations, or scripting? 18 | * Accepts true (default) or false. 19 | */ 20 | boolean doc_values() default true; 21 | 22 | /** 23 | * The date format(s) that can be parsed. Defaults to strict_date_optional_time||epoch_millis. 24 | *

25 | * https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html 26 | */ 27 | String format() default "strict_date_optional_time||epoch_millis"; 28 | 29 | /** 30 | * The locale to use when parsing dates since months do not have the same names and/or abbreviations in all languages. 31 | * The default is the ROOT locale, 32 | *

33 | * https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#ROOT 34 | */ 35 | String locale() default "ROOT"; 36 | 37 | /** 38 | * If true, malformed numbers are ignored. If false (default), 39 | * malformed numbers throw an exception and reject the whole document. 40 | */ 41 | boolean ignore_malformed() default false; 42 | 43 | /** 44 | * Whether or not the field value should be included in the _all field? Accepts true or false. 45 | * Defaults to false if index is set to false, or if a parent object field sets include_in_all to false. 46 | * Otherwise defaults to true. 47 | */ 48 | boolean include_in_all() default true; 49 | 50 | /** 51 | * Should the field be searchable? Accepts true (default) and false. 52 | */ 53 | boolean index() default true; 54 | 55 | /** 56 | * Accepts a numeric value of the same type as the field which is substituted for any explicit null values. 57 | * Defaults to null, which means the field is treated as missing. 58 | */ 59 | String null_value() default ""; 60 | 61 | /** 62 | * Whether the field value should be stored and retrievable separately from 63 | * the _source field. Accepts true or false (default). 64 | */ 65 | boolean store() default false; 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/Fielddata.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.FIELD}) 7 | @Inherited 8 | public @interface Fielddata { 9 | 10 | /** 11 | * Can the field use in-memory fielddata for sorting, aggregations, or scripting? 12 | * Accepts true or false (default). 13 | */ 14 | boolean enable() default false; 15 | 16 | /** 17 | * Expert settings which allow to decide which values to load in memory when fielddata is enabled. 18 | * By default all values are loaded. 19 | */ 20 | FielddataFrequencyFilter frequency() default @FielddataFrequencyFilter( 21 | enable = false, 22 | min = 0d, 23 | max = 0d, 24 | min_segment_size = 0 25 | ); 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/FielddataFrequencyFilter.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * Fielddata filtering can be used to reduce the number of terms loaded into memory, 7 | * and thus reduce memory usage. Terms can be filtered by frequency: 8 | *

9 | * The frequency filter allows you to only load terms whose document frequency falls between a min and max value, 10 | * which can be expressed an absolute number (when the number is bigger than 1.0) or as a percentage (eg 0.01 is 1% and 1.0 is 100%). 11 | * Frequency is calculated per segment. Percentages are based on the number of docs which have a value for the field, 12 | * as opposed to all docs in the segment. 13 | *

14 | * Small segments can be excluded completely by specifying the minimum number of docs that the segment should contain with min_segment_size 15 | *

16 | * https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html#field-data-filtering 17 | * 18 | * @author chennan 19 | */ 20 | @Retention(RetentionPolicy.RUNTIME) 21 | @Target({ElementType.FIELD}) 22 | @Inherited 23 | public @interface FielddataFrequencyFilter { 24 | 25 | boolean enable() default false; 26 | 27 | double min(); 28 | 29 | double max(); 30 | 31 | int min_segment_size(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/GeoPointField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html 7 | */ 8 | @Inherited 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target({ElementType.FIELD}) 11 | public @interface GeoPointField { 12 | /** 13 | * If true, malformed geo-points are ignored. 14 | * If false (default), malformed geo-points throw an exception and reject the whole document. 15 | */ 16 | boolean ignore_malformed() default false; 17 | } -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/IPField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface IPField { 9 | 10 | /** 11 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 12 | */ 13 | float boost() default 1.0f; 14 | 15 | /** 16 | * Should the field be stored on disk in a column-stride fashion, 17 | * so that it can later be used for sorting, aggregations, or scripting? 18 | * Accepts true (default) or false. 19 | */ 20 | boolean doc_values() default true; 21 | 22 | /** 23 | * Whether or not the field value should be included in the _all field? 24 | * Accepts true or false. Defaults to false if index is set to no, 25 | * or if a parent object field sets include_in_all to false. Otherwise defaults to true. 26 | */ 27 | boolean include_in_all() default true; 28 | 29 | /** 30 | * Should the field be searchable? Accepts true (default) or false. 31 | */ 32 | boolean index() default true; 33 | 34 | /** 35 | * Accepts a string value which is substituted for any explicit null values. 36 | * Defaults to null, which means the field is treated as missing. 37 | */ 38 | String null_value() default ""; 39 | 40 | /** 41 | * Whether the field value should be stored and retrievable separately from the _source field. 42 | * Accepts true or false (default). 43 | */ 44 | boolean store() default false; 45 | 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/MultiField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | 4 | import java.lang.annotation.*; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | @Documented 9 | public @interface MultiField { 10 | /** 11 | * The main string field 12 | */ 13 | StringField mainField(); 14 | 15 | /** 16 | * Multi-fields allow the same string value to be indexed in multiple ways for different purposes, 17 | * such as one field for search and a multi-field for sorting and aggregations. 18 | */ 19 | MultiNestedField[] fields() default {}; 20 | 21 | /** 22 | * A field of type token_count is really an integer field which accepts string values, analyzes them, 23 | * then indexes the number of tokens in the string 24 | */ 25 | TokenCountField[] tokenFields() default {}; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/MultiNestedField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.FIELD}) 10 | public @interface MultiNestedField { 11 | 12 | String name(); 13 | 14 | StringField field(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/NumberField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import org.elasticsearch.mapper.annotations.enums.NumberType; 4 | 5 | import java.lang.annotation.*; 6 | 7 | @Inherited 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.FIELD}) 10 | public @interface NumberField { 11 | /** 12 | * type of number. 13 | *

14 | * {@link NumberType} 15 | */ 16 | NumberType type(); 17 | 18 | /** 19 | * Try to convert strings to numbers and truncate fractions for integers. 20 | * Accepts true (default) and false. 21 | */ 22 | boolean coerce() default true; 23 | 24 | /** 25 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 26 | */ 27 | float boost() default 1.0f; 28 | 29 | /** 30 | * Should the field be stored on disk in a column-stride fashion, 31 | * so that it can later be used for sorting, aggregations, or scripting? 32 | * Accepts true (default) or false. 33 | */ 34 | boolean doc_values() default true; 35 | 36 | /** 37 | * If true, malformed numbers are ignored. If false (default), 38 | * malformed numbers throw an exception and reject the whole document. 39 | */ 40 | boolean ignore_malformed() default false; 41 | 42 | /** 43 | * Whether or not the field value should be included in the _all field? Accepts true or false. 44 | * Defaults to false if index is set to false, or if a parent object field sets include_in_all to false. 45 | * Otherwise defaults to true. 46 | */ 47 | boolean include_in_all() default true; 48 | 49 | /** 50 | * Should the field be searchable? Accepts true (default) and false. 51 | */ 52 | boolean index() default true; 53 | 54 | /** 55 | * Accepts a numeric value of the same type as the field which is substituted for any explicit null values. 56 | * Defaults to null, which means the field is treated as missing. 57 | */ 58 | String null_value() default ""; 59 | 60 | /** 61 | * Whether the field value should be stored and retrievable separately from 62 | * the _source field. Accepts true or false (default). 63 | */ 64 | boolean store() default false; 65 | 66 | 67 | /** 68 | * scaled_float accepts an additional parameter: 69 | *

70 | * The scaling factor to use when encoding values. Values will be multiplied by this factor at index time and rounded to the closest long value. 71 | * For instance, a scaled_float with a scaling_factor of 10 would internally store 2.34 as 23 and all 72 | * search-time operations (queries, aggregations, sorting) will behave as if the document had a value of 2.3. 73 | * High values of scaling_factor improve accuracy but also increase space requirements. This parameter is required. 74 | */ 75 | int scaling_factor() default 1; 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/PercolatorField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * The percolator field type parses a json structure into a native query and stores that query, 7 | * so that the percolate query can use it to match provided documents. 8 | *

9 | * Any field that contains a json object can be configured to be a percolator field. 10 | * The percolator field type has no settings. Just configuring the percolator field type is 11 | * sufficient to instruct Elasticsearch to treat a field as a query. 12 | *

13 | * https://www.elastic.co/guide/en/elasticsearch/reference/current/percolator.html 14 | */ 15 | @Inherited 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target({ElementType.FIELD}) 18 | public @interface PercolatorField { 19 | 20 | } -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/RangeField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import org.elasticsearch.mapper.annotations.enums.RangeType; 4 | 5 | import java.lang.annotation.*; 6 | 7 | @Inherited 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.FIELD}) 10 | public @interface RangeField { 11 | /** 12 | * type of range 13 | *

14 | * {@link RangeType} 15 | */ 16 | RangeType type(); 17 | 18 | /** 19 | * The date format(s) that can be parsed. Defaults to strict_date_optional_time||epoch_millis. 20 | *

21 | * https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html 22 | */ 23 | String format() default "strict_date_optional_time||epoch_millis"; 24 | 25 | /** 26 | * Try to convert strings to numbers and truncate fractions for integers. 27 | * Accepts true (default) and false. 28 | */ 29 | boolean coerce() default true; 30 | 31 | /** 32 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 33 | */ 34 | float boost() default 1.0f; 35 | 36 | /** 37 | * Whether or not the field value should be included in the _all field? 38 | * Accepts true or false. Defaults to false if index is set to no, 39 | * or if a parent object field sets include_in_all to false. Otherwise defaults to true. 40 | */ 41 | boolean include_in_all() default true; 42 | 43 | /** 44 | * Should the field be searchable? Accepts true (default) or false. 45 | */ 46 | boolean index() default true; 47 | 48 | 49 | /** 50 | * Whether the field value should be stored and retrievable separately from the _source field. 51 | * Accepts true or false (default). 52 | */ 53 | boolean store() default false; 54 | } -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/StringField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import org.elasticsearch.mapper.annotations.enums.IndexOptions; 4 | import org.elasticsearch.mapper.annotations.enums.SimilarityAlgorithm; 5 | import org.elasticsearch.mapper.annotations.enums.StringType; 6 | import org.elasticsearch.mapper.annotations.enums.TermVector; 7 | 8 | import java.lang.annotation.*; 9 | 10 | @Inherited 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target({ElementType.FIELD}) 13 | public @interface StringField { 14 | /** 15 | * type of string 16 | *

17 | * {@link StringType} 18 | */ 19 | StringType type(); 20 | 21 | /** 22 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 23 | */ 24 | float boost() default 1.0f; 25 | 26 | /** 27 | * The copy_to parameter allows you to create custom _all fields. In other words, 28 | * the values of multiple fields can be copied into a group field, 29 | * which can then be queried as a single field. For instance, 30 | * the first_name and last_name fields can be copied to the full_name field 31 | */ 32 | String[] copy_to() default {}; 33 | 34 | /** 35 | * Should the field be stored on disk in a column-stride fashion, 36 | * so that it can later be used for sorting, aggregations, or scripting? 37 | * Accepts true (default) or false. 38 | */ 39 | boolean doc_values() default true; 40 | 41 | /** 42 | * Should global ordinals be loaded eagerly on refresh? Accepts true or false (default). 43 | * Enabling this is a good idea on fields that are frequently used for terms aggregations. 44 | */ 45 | boolean eager_global_ordinals() default false; 46 | 47 | 48 | /** 49 | * Do not index any string longer than this value. 50 | * Defaults to 2147483647 so that all values would be accepted. 51 | */ 52 | int ignore_above() default 2147483647; 53 | 54 | /** 55 | * Whether or not the field value should be included in the _all field? 56 | * Accepts true or false. Defaults to false if index is set to no, 57 | * or if a parent object field sets include_in_all to false. Otherwise defaults to true. 58 | */ 59 | boolean include_in_all() default true; 60 | 61 | /** 62 | * Should the field be searchable? Accepts true (default) or false. 63 | */ 64 | boolean index() default true; 65 | 66 | /** 67 | * What information should be stored in the index, for scoring purposes. 68 | * Defaults to docs but can also be set to freqs to take term frequency into account when computing scores. 69 | */ 70 | IndexOptions index_options() default IndexOptions.Default; 71 | 72 | /** 73 | * Accepts a string value which is substituted for any explicit null values. 74 | * Defaults to null, which means the field is treated as missing. 75 | */ 76 | String null_value() default ""; 77 | 78 | /** 79 | * Whether the field value should be stored and retrievable separately from the _source field. 80 | * Accepts true or false (default). 81 | */ 82 | boolean store() default false; 83 | 84 | /** 85 | * Which scoring algorithm or similarity should be used. Defaults to BM25. 86 | */ 87 | SimilarityAlgorithm similarity() default SimilarityAlgorithm.Default; 88 | 89 | 90 | /** 91 | * How to pre-process the keyword prior to indexing. Defaults to null, meaning the keyword is kept as-is. 92 | * 93 | * NOTE: This functionality is experimental and may be changed or removed completely in a future release. 94 | * Elastic will take a best effort approach to fix any issues, 95 | * but experimental features are not subject to the support SLA of official GA features. 96 | */ 97 | // normalizer, not support 98 | 99 | 100 | /** 101 | * ********************************* NEXT FOR FULL TEXT ********************************* 102 | */ 103 | 104 | 105 | /** 106 | * The analyzer which should be used for analyzed string fields, both at index-time and at 107 | * search-time (unless overridden by the search_analyzer). Defaults to the default index analyzer, 108 | * or the standard analyzer. 109 | */ 110 | String analyzer() default ""; 111 | 112 | /** 113 | * The analyzer that should be used at search time on analyzed fields. Defaults to the analyzer setting. 114 | */ 115 | String search_analyzer() default ""; 116 | 117 | /** 118 | * The analyzer that should be used at search time when a phrase is encountered. Defaults to the search_analyzer setting. 119 | */ 120 | String search_quote_analyzer() default ""; 121 | 122 | /** 123 | * The number of fake term position which should be inserted between each element of an array of strings. 124 | * Defaults to the position_increment_gap configured on the analyzer which defaults to 100. 100 was chosen 125 | * because it prevents phrase queries with reasonably large slops (less than 100) from matching terms across field values. 126 | */ 127 | int position_increment_gap() default 100; 128 | 129 | /** 130 | * Whether field-length should be taken into account when scoring queries. Accepts true (default) or false. 131 | */ 132 | boolean norms() default true; 133 | 134 | /** 135 | * Can the field use in-memory fielddata for sorting, aggregations, or scripting? Accepts true or false (default). 136 | */ 137 | Fielddata fielddata() default @Fielddata; 138 | 139 | /** 140 | * Whether term vectors should be stored for an analyzed field. Defaults to no. 141 | */ 142 | TermVector term_vector() default TermVector.No; 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/fieldtype/TokenCountField.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.fieldtype; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.FIELD}) 8 | public @interface TokenCountField { 9 | 10 | String name(); 11 | 12 | /** 13 | * The analyzer which should be used to analyze the string value. 14 | * Required. For best performance, use an analyzer without token filters. 15 | */ 16 | String analyzer(); 17 | 18 | /** 19 | * Indicates if position increments should be counted. 20 | * Set to false if you don��t want to count tokens removed by analyzer filters (like stop). Defaults to true. 21 | */ 22 | boolean enable_position_increments() default true; 23 | 24 | /** 25 | * Mapping field-level query time boosting. Accepts a floating point number, defaults to 1.0. 26 | */ 27 | float boost() default 1.0f; 28 | 29 | /** 30 | * Should the field be stored on disk in a column-stride fashion, 31 | * so that it can later be used for sorting, aggregations, or scripting? 32 | * Accepts true (default) or false. 33 | */ 34 | boolean doc_values() default true; 35 | 36 | /** 37 | * Whether or not the field value should be included in the _all field? 38 | * Accepts true or false. Defaults to false if index is set to no, 39 | * or if a parent object field sets include_in_all to false. Otherwise defaults to true. 40 | */ 41 | boolean include_in_all() default true; 42 | 43 | /** 44 | * Should the field be searchable? Accepts true (default) or false. 45 | */ 46 | boolean index() default true; 47 | 48 | /** 49 | * Accepts a string value which is substituted for any explicit null values. 50 | * Defaults to null, which means the field is treated as missing. 51 | */ 52 | String null_value() default ""; 53 | 54 | /** 55 | * Whether the field value should be stored and retrievable separately from the _source field. 56 | * Accepts true or false (default). 57 | */ 58 | boolean store() default false; 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/meta/MetaField_All.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.meta; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * The _all field is a special catch-all field which concatenates 7 | * the values of all of the other fields into one big string, 8 | * using space as a delimiter, which is then analyzed and indexed, 9 | * but not stored. This means that it can be searched, but not retrieved. 10 | *

11 | * The _all field is just a text field, and accepts the same parameters that other string fields accept, 12 | * including analyzer, term_vectors, index_options, and store. 13 | *

14 | * https://www.elastic.co/guide/en/elasticsearch/reference/5.2/mapping-all-field.html 15 | * 16 | * @author chennan 17 | */ 18 | @Inherited 19 | @Retention(RetentionPolicy.RUNTIME) 20 | @Target({ElementType.TYPE}) 21 | public @interface MetaField_All { 22 | /** 23 | * The _all field can be completely disabled per-type by setting enabled to false 24 | * default set to true 25 | */ 26 | boolean enabled() default true; 27 | 28 | /** 29 | * If store is set to true, then the original field value is retrievable and can be highlighted 30 | * default set to false 31 | */ 32 | boolean store() default false; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/meta/MetaField_Parent.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.meta; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * A parent-child relationship can be established between documents 7 | * in the same index by making one mapping type the parent of another 8 | *

9 | * Parent-child restrictions 10 | * 1. The parent and child types must be different parent-child relationships 11 | * cannot be established between documents of the same type. 12 | *

13 | * 2. The _parent.type setting can only point to a type that doesn't exist yet. 14 | * This means that a type cannot become a parent type after it has been created. 15 | *

16 | * 3. Parent and child documents must be indexed on the same shard. 17 | * The parent ID is used as the routing value for the child, to ensure that the child is indexed on the same shard as the parent. 18 | * This means that the same parent value needs to be provided when getting, deleting, or updating a child document. 19 | *

20 | * https://www.elastic.co/guide/en/elasticsearch/reference/5.2/mapping-parent-field.html 21 | * 22 | * @author chennan 23 | */ 24 | @Inherited 25 | @Retention(RetentionPolicy.RUNTIME) 26 | @Target({ElementType.TYPE}) 27 | public @interface MetaField_Parent { 28 | 29 | /** 30 | * parent class 31 | */ 32 | Class[] parentClass(); 33 | 34 | /** 35 | * Parent-child uses global ordinals to speed up joins. Global ordinals need to be rebuilt after any change to a shard. 36 | * The more parent id values are stored in a shard, the longer it takes to rebuild the global ordinals for the _parent field. 37 | *

38 | * Global ordinals, by default, are built eagerly: if the index has changed, global ordinals for the _parent field 39 | * will be rebuilt as part of the refresh. This can add significant time the refresh. However most of the times this is the right trade-off, 40 | * otherwise global ordinals are rebuilt when the first parent-child query or aggregation is used. 41 | * This can introduce a significant latency spike for your users and usually this is worse as multiple global ordinals for the _parent 42 | * field may be attempt rebuilt within a single refresh interval when many writes are occurring. 43 | *

44 | * NOTE: When the parent/child is used infrequently and writes occur frequently it may make sense to disable eager loading 45 | *

46 | * default set to true 47 | */ 48 | boolean eager_global_ordinals() default true; 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/meta/MetaField_Routing.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.meta; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Inherited 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.TYPE}) 8 | public @interface MetaField_Routing { 9 | /** 10 | * A document is routed to a particular shard in an index using the following formula: 11 | *

shard_num = hash(_routing) % num_primary_shards
12 | *

13 | * Forgetting the routing value can lead to a document being indexed on more than one shard. 14 | * As a safeguard, the _routing field can be configured to make a custom routing value required for all CRUD operations 15 | *

16 | * default set to false to disable custom routing value 17 | */ 18 | boolean required() default false; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/annotations/meta/MetaField_Source.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.annotations.meta; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * The _source field contains the original JSON document body that was passed at index time. 7 | * The _source field itself is not indexed (and thus is not searchable), 8 | * but it is stored so that it can be returned when executing fetch requests, like get or search 9 | *

10 | * https://www.elastic.co/guide/en/elasticsearch/reference/5.2/mapping-source-field.html 11 | * 12 | * @author chennan 13 | */ 14 | @Inherited 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target({ElementType.TYPE}) 17 | public @interface MetaField_Source { 18 | /** 19 | * default set to true 20 | */ 21 | boolean enabled() default true; 22 | 23 | 24 | /** 25 | * An expert-only feature is the ability to prune the contents of the _source field 26 | * after the document has been indexed, but before the _source field is stored. 27 | *

28 | * Removing fields from the _source has similar downsides to disabling _source, 29 | * especially the fact that you cannot reindex documents from one Elasticsearch index to another. 30 | * Consider using source filtering instead. 31 | *

32 | * The includes/excludes parameters (which also accept wildcards) can be used as follows: 33 | *

34 | *

35 |      * PUT logs
36 |      * {
37 |      *   "mappings": {
38 |      *     "event": {
39 |      *       "_source": {
40 |      *         "includes": [
41 |      *           "*.count",
42 |      *           "meta.*"
43 |      *         ],
44 |      *         "excludes": [
45 |      *           "meta.description",
46 |      *           "meta.other.*"
47 |      *         ]
48 |      *        }
49 |      *      }
50 |      *    }
51 |      * }
52 |      * 
53 | */ 54 | String[] includes() default {}; 55 | 56 | String[] excludes() default {}; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/bean/GeoPoint.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.bean; 2 | 3 | public class GeoPoint { 4 | /** 5 | * latitude 6 | */ 7 | private float lat; 8 | /** 9 | * longitude 10 | */ 11 | private float lon; 12 | 13 | public float getLat() { 14 | return lat; 15 | } 16 | 17 | public void setLat(float lat) { 18 | this.lat = lat; 19 | } 20 | 21 | public float getLon() { 22 | return lon; 23 | } 24 | 25 | public void setLon(float lon) { 26 | this.lon = lon; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/bean/Range.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.bean; 2 | 3 | public class Range { 4 | 5 | DataType gte; 6 | 7 | DataType lte; 8 | 9 | public DataType getGte() { 10 | return gte; 11 | } 12 | 13 | public void setGte(DataType gte) { 14 | this.gte = gte; 15 | } 16 | 17 | public DataType getLte() { 18 | return lte; 19 | } 20 | 21 | public void setLte(DataType lte) { 22 | this.lte = lte; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/BinaryFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.BinaryField; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Field; 8 | 9 | public class BinaryFieldMapper { 10 | 11 | public static boolean isValidBinaryType(Field field) { 12 | Class fieldClass = field.getType(); 13 | return String.class.isAssignableFrom(fieldClass) && field.isAnnotationPresent(BinaryField.class); 14 | } 15 | 16 | 17 | public static void mapDataType(XContentBuilder mappingBuilder, BinaryField booleanField) throws IOException { 18 | mappingBuilder.field("type", "binary"); 19 | 20 | if (!booleanField.doc_values()) { 21 | mappingBuilder.field("doc_values", booleanField.doc_values()); 22 | } 23 | 24 | if (booleanField.store()) { 25 | mappingBuilder.field("store", booleanField.store()); 26 | } 27 | } 28 | 29 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 30 | if (!isValidBinaryType(field)) { 31 | throw new IllegalArgumentException( 32 | String.format("field type[%s] is invalid type of binary.", field.getType())); 33 | } 34 | 35 | if (field.isAnnotationPresent(BinaryField.class)) { 36 | BinaryField binaryField = field.getDeclaredAnnotation(BinaryField.class); 37 | mapDataType(mappingBuilder, binaryField); 38 | return; 39 | } 40 | 41 | mappingBuilder.field("type", "binary"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/BooleanFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.BooleanField; 5 | import org.elasticsearch.mapper.utils.BeanUtils; 6 | import org.elasticsearch.mapper.utils.StringUtils; 7 | 8 | import java.io.IOException; 9 | import java.lang.reflect.Field; 10 | 11 | public class BooleanFieldMapper { 12 | 13 | public static boolean isValidBooleanType(Field field) { 14 | if (BeanUtils.isCollectionType(field)) { 15 | if (!BeanUtils.isValidCollectionType(field)) { 16 | throw new IllegalArgumentException( 17 | String.format("Unsupported list class type, name[%s].", field.getName())); 18 | } 19 | 20 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 21 | return Boolean.class == genericTypeClass || boolean.class == genericTypeClass; 22 | } 23 | 24 | Class fieldClass = field.getType(); 25 | return Boolean.class == fieldClass || boolean.class == fieldClass; 26 | } 27 | 28 | 29 | public static void mapDataType(XContentBuilder mappingBuilder, BooleanField booleanField) throws IOException { 30 | mappingBuilder.field("type", "boolean"); 31 | if (booleanField.boost() != 1.0f) { 32 | mappingBuilder.field("boost", booleanField.boost()); 33 | } 34 | 35 | if (!booleanField.doc_values()) { 36 | mappingBuilder.field("doc_values", booleanField.doc_values()); 37 | } 38 | 39 | if (!booleanField.index()) { 40 | mappingBuilder.field("index", booleanField.index()); 41 | } 42 | 43 | if (StringUtils.isNotBlank(booleanField.null_value())) { 44 | mappingBuilder.field("null_value", booleanField.null_value()); 45 | } 46 | 47 | if (booleanField.store()) { 48 | mappingBuilder.field("store", booleanField.store()); 49 | } 50 | } 51 | 52 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 53 | if (!isValidBooleanType(field)) { 54 | throw new IllegalArgumentException( 55 | String.format("field type[%s] is invalid type of boolean.", field.getType())); 56 | } 57 | 58 | if (field.isAnnotationPresent(BooleanField.class)) { 59 | BooleanField booleanField = field.getDeclaredAnnotation(BooleanField.class); 60 | mapDataType(mappingBuilder, booleanField); 61 | return; 62 | } 63 | 64 | mappingBuilder.field("type", "boolean"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/CompletionFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.CompletionContext; 5 | import org.elasticsearch.mapper.annotations.fieldtype.CompletionField; 6 | import org.elasticsearch.mapper.utils.BeanUtils; 7 | import org.elasticsearch.mapper.utils.StringUtils; 8 | 9 | import java.io.IOException; 10 | import java.lang.reflect.Field; 11 | 12 | public class CompletionFieldMapper { 13 | 14 | public static boolean isValidCompletionFieldType(Field field) { 15 | if (BeanUtils.isCollectionType(field)) { 16 | if (!BeanUtils.isValidCollectionType(field)) { 17 | throw new IllegalArgumentException( 18 | String.format("Unsupported list class type, name[%s].", field.getName())); 19 | } 20 | 21 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 22 | return String.class.isAssignableFrom(genericTypeClass) && field.isAnnotationPresent(CompletionField.class); 23 | } 24 | 25 | Class fieldClass = field.getType(); 26 | return String.class.isAssignableFrom(fieldClass) && field.isAnnotationPresent(CompletionField.class); 27 | } 28 | 29 | public static void mapDataType(XContentBuilder mappingBuilder, CompletionField completionField) throws IOException { 30 | mappingBuilder.field("type", "completion"); 31 | 32 | if (!"simple".equalsIgnoreCase(completionField.analyzer())) { 33 | mappingBuilder.field("analyzer", completionField.analyzer()); 34 | } 35 | 36 | if (!"simple".equalsIgnoreCase(completionField.search_analyzer())) { 37 | mappingBuilder.field("search_analyzer", completionField.search_analyzer()); 38 | } 39 | 40 | if (!completionField.preserve_separators()) { 41 | mappingBuilder.field("preserve_separators", completionField.preserve_separators()); 42 | } 43 | 44 | if (!completionField.preserve_position_increments()) { 45 | mappingBuilder.field("preserve_position_increments", completionField.preserve_position_increments()); 46 | } 47 | 48 | if (completionField.max_input_length() != 50) { 49 | mappingBuilder.field("max_input_length", completionField.max_input_length()); 50 | } 51 | 52 | if (completionField.contexts().length > 0) { 53 | mappingBuilder.startArray("contexts"); 54 | for (CompletionContext completionContext : completionField.contexts()) { 55 | mappingBuilder.field("name", completionContext.name()); 56 | mappingBuilder.field("type", completionContext.type()); 57 | 58 | if (StringUtils.isNotBlank(completionContext.path())) { 59 | mappingBuilder.field("path", completionContext.path()); 60 | } 61 | } 62 | mappingBuilder.endArray(); 63 | } 64 | } 65 | 66 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 67 | if (!isValidCompletionFieldType(field)) { 68 | throw new IllegalArgumentException( 69 | String.format("field type[%s] is invalid type of string.", field.getType())); 70 | } 71 | 72 | CompletionField completionField = field.getDeclaredAnnotation(CompletionField.class); 73 | mapDataType(mappingBuilder, completionField); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/DateFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.DateField; 5 | import org.elasticsearch.mapper.utils.BeanUtils; 6 | import org.elasticsearch.mapper.utils.StringUtils; 7 | 8 | import java.io.IOException; 9 | import java.lang.reflect.Field; 10 | import java.util.Date; 11 | 12 | public class DateFieldMapper { 13 | 14 | public static boolean isValidDateType(Field field) { 15 | if (BeanUtils.isCollectionType(field)) { 16 | if (!BeanUtils.isValidCollectionType(field)) { 17 | throw new IllegalArgumentException( 18 | String.format("Unsupported list class type, name[%s].", field.getName())); 19 | } 20 | 21 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 22 | return Date.class.isAssignableFrom(genericTypeClass); 23 | } 24 | 25 | Class fieldClass = field.getType(); 26 | return Date.class.isAssignableFrom(fieldClass); 27 | } 28 | 29 | public static void mapDataType(XContentBuilder mappingBuilder, DateField dateField) throws IOException { 30 | mappingBuilder.field("type", "date"); 31 | mappingBuilder.field("format", dateField.format()); 32 | 33 | if (dateField.boost() != 1.0f) { 34 | mappingBuilder.field("boost", dateField.boost()); 35 | } 36 | 37 | if (!dateField.doc_values()) { 38 | mappingBuilder.field("doc_values", dateField.doc_values()); 39 | } 40 | 41 | if (dateField.ignore_malformed()) { 42 | mappingBuilder.field("ignore_malformed", dateField.ignore_malformed()); 43 | } 44 | 45 | if (!dateField.include_in_all()) { 46 | mappingBuilder.field("include_in_all", dateField.include_in_all()); 47 | } 48 | else if (!dateField.index()) { 49 | mappingBuilder.field("include_in_all", false); 50 | } 51 | 52 | if (!dateField.index()) { 53 | mappingBuilder.field("index", dateField.index()); 54 | } 55 | 56 | if (StringUtils.isNotBlank(dateField.null_value())) { 57 | mappingBuilder.field("null_value", dateField.null_value()); 58 | } 59 | 60 | if (dateField.store()) { 61 | mappingBuilder.field("store", dateField.store()); 62 | } 63 | 64 | if (!"ROOT".equalsIgnoreCase(dateField.locale())) { 65 | mappingBuilder.field("locale", dateField.locale()); 66 | } 67 | } 68 | 69 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 70 | if (!isValidDateType(field)) { 71 | throw new IllegalArgumentException( 72 | String.format("field type[%s] is invalid type of date.", field.getType())); 73 | } 74 | 75 | if (field.isAnnotationPresent(DateField.class)) { 76 | DateField dateField = field.getDeclaredAnnotation(DateField.class); 77 | mapDataType(mappingBuilder, dateField); 78 | return; 79 | } 80 | 81 | mappingBuilder.field("type", "date"); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/GeoPointFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.GeoPointField; 5 | import org.elasticsearch.mapper.bean.GeoPoint; 6 | 7 | import java.io.IOException; 8 | import java.lang.reflect.Field; 9 | 10 | public class GeoPointFieldMapper { 11 | 12 | public static void main(String[] args) { 13 | Class cls = String.class; 14 | } 15 | 16 | public static boolean isValidGeoPointFieldType(Field field) { 17 | Class fieldClass = field.getType(); 18 | 19 | if (!field.isAnnotationPresent(GeoPointField.class)) { 20 | return false; 21 | } 22 | 23 | return String.class.isAssignableFrom(fieldClass) || fieldClass == GeoPoint.class; 24 | 25 | } 26 | 27 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 28 | if (!isValidGeoPointFieldType(field)) { 29 | throw new IllegalArgumentException( 30 | String.format("field type[%s] is invalid type of percolator.", field.getType())); 31 | } 32 | 33 | mappingBuilder.field("type", "geo_point"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/IPFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.IPField; 5 | import org.elasticsearch.mapper.utils.StringUtils; 6 | 7 | import java.io.IOException; 8 | import java.lang.reflect.Field; 9 | 10 | public class IPFieldMapper { 11 | 12 | public static boolean isValidIPFieldType(Field field) { 13 | Class fieldClass = field.getType(); 14 | return String.class.isAssignableFrom(fieldClass) && field.isAnnotationPresent(IPField.class); 15 | } 16 | 17 | public static void mapDataType(XContentBuilder mappingBuilder, IPField ipField) throws IOException { 18 | mappingBuilder.field("type", "ip"); 19 | 20 | if (ipField.boost() != 1.0f) { 21 | mappingBuilder.field("boost", ipField.boost()); 22 | } 23 | 24 | if (!ipField.doc_values()) { 25 | mappingBuilder.field("doc_values", ipField.doc_values()); 26 | } 27 | 28 | if (!ipField.include_in_all()) { 29 | mappingBuilder.field("include_in_all", ipField.include_in_all()); 30 | } 31 | else if (!ipField.index()) { 32 | mappingBuilder.field("include_in_all", false); 33 | } 34 | 35 | if (!ipField.index()) { 36 | mappingBuilder.field("index", ipField.index()); 37 | } 38 | 39 | if (StringUtils.isNotBlank(ipField.null_value())) { 40 | mappingBuilder.field("null_value", ipField.null_value()); 41 | } 42 | 43 | if (ipField.store()) { 44 | mappingBuilder.field("store", ipField.store()); 45 | } 46 | } 47 | 48 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 49 | if (!isValidIPFieldType(field)) { 50 | throw new IllegalArgumentException( 51 | String.format("field type[%s] is invalid type of ip.", field.getType())); 52 | } 53 | 54 | IPField ipField = field.getDeclaredAnnotation(IPField.class); 55 | mapDataType(mappingBuilder, ipField); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/MappingBuilder.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | 4 | import com.google.common.collect.Maps; 5 | import org.elasticsearch.common.xcontent.XContentBuilder; 6 | import org.elasticsearch.common.xcontent.XContentFactory; 7 | import org.elasticsearch.mapper.annotations.Document; 8 | import org.elasticsearch.mapper.annotations.IgnoreField; 9 | import org.elasticsearch.mapper.utils.BeanUtils; 10 | 11 | import java.io.IOException; 12 | import java.lang.reflect.Field; 13 | import java.lang.reflect.Modifier; 14 | import java.lang.reflect.Type; 15 | import java.util.Map; 16 | 17 | public class MappingBuilder { 18 | 19 | public Map buildMappingAsString(Class documentClazz) throws IOException { 20 | Map mappingMap = buildMapping(documentClazz); 21 | Map stringMappingMap = Maps.newLinkedHashMap(); 22 | for (String key : mappingMap.keySet()) { 23 | stringMappingMap.put(key, mappingMap.get(key).string()); 24 | } 25 | return stringMappingMap; 26 | } 27 | 28 | public Map buildMapping(Class documentClazz) throws IOException { 29 | if (documentClazz == null) { 30 | throw new IllegalArgumentException("param[documentClazz] can not be null!"); 31 | } 32 | 33 | if (!documentClazz.isAnnotationPresent(Document.class)) { 34 | throw new IllegalArgumentException( 35 | String.format("Can't find annotation[@Document] at class[%s]", documentClazz.getName())); 36 | } 37 | 38 | Map mappingMap = Maps.newLinkedHashMap(); 39 | buildMapping(documentClazz, mappingMap); 40 | return mappingMap; 41 | } 42 | 43 | private void buildMapping(Class documentClazz, Map mappingMap) throws IOException { 44 | XContentBuilder mappingBuilder = XContentFactory.jsonBuilder().prettyPrint().startObject(); 45 | Document document = documentClazz.getAnnotation(Document.class); 46 | 47 | if (document == null) { 48 | throw new IllegalStateException( 49 | String.format("Can't find annotation[@Document] at class[%s]", documentClazz.getName())); 50 | } 51 | 52 | if (document._parent().parentClass().length > 0) { 53 | Class parentClass = document._parent().parentClass()[0]; 54 | buildMapping(parentClass, mappingMap); 55 | } 56 | 57 | String indexType = document._type(); 58 | mappingMap.put(indexType, mappingBuilder); 59 | 60 | mappingBuilder.startObject(indexType); 61 | 62 | buildTypeSetting(mappingBuilder, documentClazz); 63 | buildTypeProperty(mappingBuilder, documentClazz); 64 | 65 | mappingBuilder.endObject(); 66 | mappingBuilder.endObject(); 67 | } 68 | 69 | private void buildTypeSetting(XContentBuilder mapping, Class clazz) throws IOException { 70 | Document document = (Document) clazz.getAnnotation(Document.class); 71 | 72 | if (!document._all().enabled()) { 73 | mapping.startObject("_all").field("enabled", false).endObject(); 74 | } 75 | 76 | if (document._routing().required()) { 77 | mapping.startObject("_routing").field("required", true).endObject(); 78 | } 79 | 80 | if (!document.dynamic()) { 81 | mapping.field("dynamic", false); 82 | } 83 | 84 | if (document._parent().parentClass().length > 0) { 85 | Class parentClass = document._parent().parentClass()[0]; 86 | Document parentTypeSetting = parentClass.getAnnotation(Document.class); 87 | mapping.startObject("_parent") 88 | .field("type", parentTypeSetting._type()); 89 | 90 | if (!document._parent().eager_global_ordinals()) { 91 | mapping.field("eager_global_ordinals", false); 92 | } 93 | mapping.endObject(); 94 | } 95 | } 96 | 97 | private XContentBuilder buildTypeProperty(XContentBuilder mappingBuilder, Class clazz) throws IOException { 98 | mappingBuilder.startObject("properties"); 99 | 100 | Field[] classFields = BeanUtils.retrieveFields(clazz); 101 | for (Field classField : classFields) { 102 | String fieldName = classField.getName(); 103 | 104 | if (Modifier.isTransient(classField.getModifiers()) 105 | || Modifier.isStatic(classField.getModifiers()) 106 | || fieldName.equals("$VRc") || fieldName.equals("serialVersionUID")) { 107 | continue; 108 | } 109 | 110 | if (classField.getAnnotation(IgnoreField.class) != null) { 111 | continue; 112 | } 113 | 114 | buildFieldProperty(mappingBuilder, classField); 115 | } 116 | 117 | mappingBuilder.endObject(); 118 | 119 | return mappingBuilder; 120 | } 121 | 122 | private XContentBuilder buildFieldProperty(XContentBuilder mappingBuilder, Field field) throws IOException { 123 | mappingBuilder.startObject(field.getName()); 124 | 125 | // Geo point field 126 | if (GeoPointFieldMapper.isValidGeoPointFieldType(field)) { 127 | GeoPointFieldMapper.mapDataType(mappingBuilder, field); 128 | } 129 | // Percolator field 130 | else if (PercolatorFieldMapper.isValidPercolatorFieldType(field)) { 131 | PercolatorFieldMapper.mapDataType(mappingBuilder, field); 132 | } 133 | // IP field 134 | else if (IPFieldMapper.isValidIPFieldType(field)) { 135 | IPFieldMapper.mapDataType(mappingBuilder, field); 136 | } 137 | // Range field 138 | else if (RangeFieldMapper.isValidRangeFieldType(field)) { 139 | RangeFieldMapper.mapDataType(mappingBuilder, field); 140 | } 141 | // Number 142 | else if (NumericFieldMapper.isValidNumberType(field)) { 143 | NumericFieldMapper.mapDataType(mappingBuilder, field); 144 | } 145 | // Boolean 146 | else if (BooleanFieldMapper.isValidBooleanType(field)) { 147 | BooleanFieldMapper.mapDataType(mappingBuilder, field); 148 | } 149 | // Binary field 150 | else if (BinaryFieldMapper.isValidBinaryType(field)) { 151 | BinaryFieldMapper.mapDataType(mappingBuilder, field); 152 | } 153 | // Multi field 154 | else if (MultiFieldMapper.isValidMultiFieldType(field)) { 155 | MultiFieldMapper.mapDataType(mappingBuilder, field); 156 | } 157 | // Completion Field 158 | else if (CompletionFieldMapper.isValidCompletionFieldType(field)) { 159 | CompletionFieldMapper.mapDataType(mappingBuilder, field); 160 | } 161 | // String field 162 | else if (StringFieldMapper.isValidStringFieldType(field)) { 163 | StringFieldMapper.mapDataType(mappingBuilder, field); 164 | } 165 | // Date field 166 | else if (DateFieldMapper.isValidDateType(field)) { 167 | DateFieldMapper.mapDataType(mappingBuilder, field); 168 | } 169 | // Collection type field 170 | else if (BeanUtils.isCollectionType(field)) { 171 | if (!BeanUtils.isValidCollectionType(field)) { 172 | throw new IllegalArgumentException( 173 | String.format("Unsupported list class type, name[%s].", field.getName())); 174 | } 175 | Type genericType = BeanUtils.getCollectionGenericType(field); 176 | 177 | //Nested Doc Type 178 | mappingBuilder.field("type", "nested"); 179 | buildTypeProperty(mappingBuilder, (Class) genericType); 180 | } 181 | //Inner Doc Type 182 | else { 183 | mappingBuilder.field("type", "object"); 184 | buildTypeProperty(mappingBuilder, field.getType()); 185 | } 186 | mappingBuilder.endObject(); 187 | 188 | return mappingBuilder; 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/MultiFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.MultiField; 5 | import org.elasticsearch.mapper.annotations.fieldtype.MultiNestedField; 6 | import org.elasticsearch.mapper.annotations.fieldtype.TokenCountField; 7 | 8 | import java.io.IOException; 9 | import java.lang.reflect.Field; 10 | 11 | public class MultiFieldMapper { 12 | 13 | public static boolean isValidMultiFieldType(Field field) { 14 | Class fieldClass = field.getType(); 15 | return String.class.isAssignableFrom(fieldClass) && field.isAnnotationPresent(MultiField.class); 16 | } 17 | 18 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 19 | if (!isValidMultiFieldType(field)) { 20 | throw new IllegalArgumentException( 21 | String.format("field type[%s] is invalid type of string.", field.getType())); 22 | } 23 | MultiField multiField = field.getDeclaredAnnotation(MultiField.class); 24 | StringFieldMapper.mapDataType(mappingBuilder, multiField.mainField()); 25 | 26 | mappingBuilder.startObject("fields"); 27 | 28 | for (MultiNestedField otherField : multiField.fields()) { 29 | mappingBuilder.startObject(otherField.name()); 30 | StringFieldMapper.mapDataType(mappingBuilder, otherField.field()); 31 | mappingBuilder.endObject(); 32 | } 33 | 34 | for (TokenCountField tokenCountField : multiField.tokenFields()) { 35 | mappingBuilder.startObject(tokenCountField.name()); 36 | TokenCountFieldMapper.mapDataType(mappingBuilder, tokenCountField); 37 | mappingBuilder.endObject(); 38 | } 39 | 40 | mappingBuilder.endObject(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/NumericFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import org.elasticsearch.common.xcontent.XContentBuilder; 5 | import org.elasticsearch.mapper.annotations.enums.NumberType; 6 | import org.elasticsearch.mapper.annotations.fieldtype.NumberField; 7 | import org.elasticsearch.mapper.utils.BeanUtils; 8 | import org.elasticsearch.mapper.utils.StringUtils; 9 | 10 | import java.io.IOException; 11 | import java.lang.reflect.Field; 12 | import java.math.BigDecimal; 13 | import java.math.BigInteger; 14 | import java.util.Map; 15 | 16 | public class NumericFieldMapper { 17 | 18 | private static final Map validNumericFieldClassMap = ImmutableMap.builder() 19 | .put(Double.class, NumberType.Double) 20 | .put(BigDecimal.class, NumberType.Double) 21 | .put(Float.class, NumberType.Float) 22 | .put(Long.class, NumberType.Long) 23 | .put(BigInteger.class, NumberType.Long) 24 | .put(Integer.class, NumberType.Integer) 25 | .put(Short.class, NumberType.Short) 26 | .put(Byte.class, NumberType.Byte) 27 | 28 | .put(double.class, NumberType.Double) 29 | .put(float.class, NumberType.Float) 30 | .put(long.class, NumberType.Long) 31 | .put(int.class, NumberType.Integer) 32 | .put(short.class, NumberType.Short) 33 | .put(byte.class, NumberType.Byte) 34 | .build(); 35 | 36 | public static boolean isValidNumberType(Field field) { 37 | if (BeanUtils.isCollectionType(field)) { 38 | if (!BeanUtils.isValidCollectionType(field)) { 39 | throw new IllegalArgumentException( 40 | String.format("Unsupported more than one collection generic type, name[%s].", field.getName())); 41 | } 42 | 43 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 44 | return validNumericFieldClassMap.keySet().contains(genericTypeClass); 45 | } 46 | 47 | 48 | Class fieldClass = field.getType(); 49 | return validNumericFieldClassMap.keySet().contains(fieldClass); 50 | } 51 | 52 | public static void mapDataType(XContentBuilder mappingBuilder, NumberField numberField) throws IOException { 53 | mappingBuilder.field("type", numberField.type().code()); 54 | 55 | if (!numberField.coerce()) { 56 | mappingBuilder.field("coerce", numberField.coerce()); 57 | } 58 | 59 | if (numberField.boost() != 1.0f) { 60 | mappingBuilder.field("boost", numberField.boost()); 61 | } 62 | 63 | if (!numberField.doc_values()) { 64 | mappingBuilder.field("doc_values", numberField.doc_values()); 65 | } 66 | 67 | if (numberField.ignore_malformed()) { 68 | mappingBuilder.field("ignore_malformed", numberField.ignore_malformed()); 69 | } 70 | 71 | if (!numberField.include_in_all()) { 72 | mappingBuilder.field("include_in_all", numberField.include_in_all()); 73 | } 74 | else if (!numberField.index()) { 75 | mappingBuilder.field("include_in_all", false); 76 | } 77 | 78 | if (!numberField.index()) { 79 | mappingBuilder.field("index", numberField.index()); 80 | } 81 | 82 | if (StringUtils.isNotBlank(numberField.null_value())) { 83 | mappingBuilder.field("null_value", numberField.null_value()); 84 | } 85 | 86 | if (numberField.store()) { 87 | mappingBuilder.field("store", numberField.store()); 88 | } 89 | 90 | if (numberField.type() == NumberType.ScaledFloat && numberField.scaling_factor() != 1) { 91 | mappingBuilder.field("scaling_factor", numberField.scaling_factor()); 92 | } 93 | } 94 | 95 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 96 | if (!isValidNumberType(field)) { 97 | throw new IllegalArgumentException( 98 | String.format("field type[%s] is invalid type of number.", field.getType())); 99 | } 100 | 101 | if (field.isAnnotationPresent(NumberField.class)) { 102 | NumberField numberField = field.getDeclaredAnnotation(NumberField.class); 103 | mapDataType(mappingBuilder, numberField); 104 | return; 105 | } 106 | 107 | if (BeanUtils.isCollectionType(field)) { 108 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 109 | NumberType numberType = validNumericFieldClassMap.get(genericTypeClass); 110 | mappingBuilder.field("type", numberType.code()); 111 | } 112 | else { 113 | NumberType numberType = validNumericFieldClassMap.get(field.getType()); 114 | mappingBuilder.field("type", numberType.code()); 115 | } 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/PercolatorFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.PercolatorField; 5 | 6 | import java.io.IOException; 7 | import java.lang.reflect.Field; 8 | 9 | public class PercolatorFieldMapper { 10 | 11 | public static boolean isValidPercolatorFieldType(Field field) { 12 | Class fieldClass = field.getType(); 13 | return String.class.isAssignableFrom(fieldClass) && field.isAnnotationPresent(PercolatorField.class); 14 | } 15 | 16 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 17 | if (!isValidPercolatorFieldType(field)) { 18 | throw new IllegalArgumentException( 19 | String.format("field type[%s] is invalid type of percolator.", field.getType())); 20 | } 21 | 22 | mappingBuilder.field("type", "percolator"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/RangeFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.enums.RangeType; 5 | import org.elasticsearch.mapper.annotations.fieldtype.RangeField; 6 | 7 | import java.io.IOException; 8 | import java.lang.reflect.Field; 9 | 10 | public class RangeFieldMapper { 11 | 12 | public static boolean isValidRangeFieldType(Field field) { 13 | return field.isAnnotationPresent(RangeField.class); 14 | } 15 | 16 | public static void mapDataType(XContentBuilder mappingBuilder, RangeField rangeField) throws IOException { 17 | mappingBuilder.field("type", rangeField.type().code()); 18 | 19 | if (rangeField.type() == RangeType.DateRange) { 20 | mappingBuilder.field("format", rangeField.format()); 21 | } 22 | 23 | if (!rangeField.coerce()) { 24 | mappingBuilder.field("coerce", rangeField.coerce()); 25 | } 26 | 27 | if (rangeField.boost() != 1.0f) { 28 | mappingBuilder.field("boost", rangeField.boost()); 29 | } 30 | 31 | if (!rangeField.include_in_all()) { 32 | mappingBuilder.field("include_in_all", rangeField.include_in_all()); 33 | } 34 | else if (!rangeField.index()) { 35 | mappingBuilder.field("include_in_all", false); 36 | } 37 | 38 | if (!rangeField.index()) { 39 | mappingBuilder.field("index", rangeField.index()); 40 | } 41 | 42 | if (rangeField.store()) { 43 | mappingBuilder.field("store", rangeField.store()); 44 | } 45 | 46 | } 47 | 48 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 49 | if (!isValidRangeFieldType(field)) { 50 | throw new IllegalArgumentException( 51 | String.format("field type[%s] is invalid type of range.", field.getType())); 52 | } 53 | RangeField stringField = field.getDeclaredAnnotation(RangeField.class); 54 | mapDataType(mappingBuilder, stringField); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/StringFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.enums.IndexOptions; 5 | import org.elasticsearch.mapper.annotations.enums.SimilarityAlgorithm; 6 | import org.elasticsearch.mapper.annotations.enums.StringType; 7 | import org.elasticsearch.mapper.annotations.enums.TermVector; 8 | import org.elasticsearch.mapper.annotations.fieldtype.StringField; 9 | import org.elasticsearch.mapper.utils.BeanUtils; 10 | import org.elasticsearch.mapper.utils.StringUtils; 11 | 12 | import java.io.IOException; 13 | import java.lang.reflect.Field; 14 | 15 | public class StringFieldMapper { 16 | 17 | public static boolean isValidStringFieldType(Field field) { 18 | if (BeanUtils.isCollectionType(field)) { 19 | if (!BeanUtils.isValidCollectionType(field)) { 20 | throw new IllegalArgumentException( 21 | String.format("Unsupported list class type, name[%s].", field.getName())); 22 | } 23 | 24 | Class genericTypeClass = (Class) BeanUtils.getCollectionGenericType(field); 25 | return String.class.isAssignableFrom(genericTypeClass); 26 | } 27 | 28 | Class fieldClass = field.getType(); 29 | return String.class.isAssignableFrom(fieldClass); 30 | } 31 | 32 | public static void mapDataType(XContentBuilder mappingBuilder, StringField stringField) throws IOException { 33 | mappingBuilder.field("type", stringField.type().code()); 34 | 35 | if (stringField.boost() != 1.0f) { 36 | mappingBuilder.field("boost", stringField.boost()); 37 | } 38 | 39 | if (!stringField.doc_values()) { 40 | mappingBuilder.field("doc_values", stringField.doc_values()); 41 | } 42 | 43 | if (stringField.copy_to().length > 0) { 44 | if (stringField.copy_to().length == 1) { 45 | mappingBuilder.field("copy_to", stringField.copy_to()[0]); 46 | } 47 | else { 48 | mappingBuilder.array("copy_to", stringField.copy_to()); 49 | } 50 | } 51 | 52 | if (stringField.eager_global_ordinals()) { 53 | mappingBuilder.field("eager_global_ordinals", stringField.eager_global_ordinals()); 54 | } 55 | 56 | if (stringField.ignore_above() != 2147483647) { 57 | mappingBuilder.field("ignore_above", stringField.ignore_above()); 58 | } 59 | 60 | if (!stringField.include_in_all()) { 61 | mappingBuilder.field("include_in_all", stringField.include_in_all()); 62 | } 63 | else if (!stringField.index()) { 64 | mappingBuilder.field("include_in_all", false); 65 | } 66 | 67 | if (!stringField.index()) { 68 | mappingBuilder.field("index", stringField.index()); 69 | } 70 | 71 | if (stringField.index_options() != IndexOptions.Default) { 72 | mappingBuilder.field("index_options", stringField.index_options().code()); 73 | } 74 | 75 | if (StringUtils.isNotBlank(stringField.null_value())) { 76 | mappingBuilder.field("null_value", stringField.null_value()); 77 | } 78 | 79 | if (stringField.store()) { 80 | mappingBuilder.field("store", stringField.store()); 81 | } 82 | 83 | if (stringField.similarity() != SimilarityAlgorithm.Default) { 84 | mappingBuilder.field("similarity", stringField.similarity().code()); 85 | } 86 | 87 | // full text setting 88 | if (stringField.type() == StringType.Text) { 89 | 90 | if (StringUtils.isNotBlank(stringField.analyzer())) { 91 | mappingBuilder.field("analyzer", stringField.analyzer()); 92 | } 93 | 94 | if (StringUtils.isNotBlank(stringField.search_analyzer())) { 95 | mappingBuilder.field("analyzer", stringField.search_analyzer()); 96 | } 97 | 98 | if (StringUtils.isNotBlank(stringField.search_quote_analyzer())) { 99 | mappingBuilder.field("analyzer", stringField.search_quote_analyzer()); 100 | } 101 | 102 | if (stringField.position_increment_gap() != 100) { 103 | mappingBuilder.field("position_increment_gap", stringField.position_increment_gap()); 104 | } 105 | 106 | if (!stringField.norms()) { 107 | mappingBuilder.field("norms", stringField.norms()); 108 | } 109 | 110 | if (stringField.term_vector() != TermVector.No) { 111 | mappingBuilder.field("term_vector", stringField.term_vector().code()); 112 | } 113 | 114 | if (stringField.fielddata().enable() && stringField.type() == StringType.Text) { 115 | mappingBuilder.field("fielddata", stringField.fielddata().enable()); 116 | 117 | if (stringField.fielddata().frequency().enable()) { 118 | 119 | mappingBuilder.startObject("fielddata_frequency_filter"); 120 | 121 | if (stringField.fielddata().frequency().min() > 0) { 122 | mappingBuilder.field("min", stringField.fielddata().frequency().min()); 123 | } 124 | 125 | if (stringField.fielddata().frequency().max() > 0) { 126 | mappingBuilder.field("max", stringField.fielddata().frequency().max()); 127 | } 128 | 129 | if (stringField.fielddata().frequency().min_segment_size() > 0) { 130 | mappingBuilder.field("min_segment_size", stringField.fielddata().frequency().min_segment_size()); 131 | } 132 | 133 | mappingBuilder.endObject(); 134 | } 135 | } 136 | } 137 | } 138 | 139 | public static void mapDataType(XContentBuilder mappingBuilder, Field field) throws IOException { 140 | if (!isValidStringFieldType(field)) { 141 | throw new IllegalArgumentException( 142 | String.format("field type[%s] is invalid type of string.", field.getType())); 143 | } 144 | 145 | if (field.isAnnotationPresent(StringField.class)) { 146 | StringField stringField = field.getDeclaredAnnotation(StringField.class); 147 | mapDataType(mappingBuilder, stringField); 148 | 149 | return; 150 | } 151 | 152 | mappingBuilder.field("type", "keyword"); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/mapper/TokenCountFieldMapper.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.mapper; 2 | 3 | import org.elasticsearch.common.xcontent.XContentBuilder; 4 | import org.elasticsearch.mapper.annotations.fieldtype.TokenCountField; 5 | import org.elasticsearch.mapper.utils.StringUtils; 6 | 7 | import java.io.IOException; 8 | 9 | public class TokenCountFieldMapper { 10 | 11 | public static void mapDataType(XContentBuilder mappingBuilder, TokenCountField tokenCountField) throws IOException { 12 | mappingBuilder.field("type", "token_count"); 13 | mappingBuilder.field("analyzer", tokenCountField.analyzer()); 14 | 15 | if (!tokenCountField.enable_position_increments()) { 16 | mappingBuilder.field("enable_position_increments", tokenCountField.enable_position_increments()); 17 | } 18 | 19 | if (tokenCountField.boost() != 1.0f) { 20 | mappingBuilder.field("boost", tokenCountField.boost()); 21 | } 22 | 23 | if (!tokenCountField.doc_values()) { 24 | mappingBuilder.field("doc_values", tokenCountField.doc_values()); 25 | } 26 | 27 | if (!tokenCountField.include_in_all()) { 28 | mappingBuilder.field("include_in_all", tokenCountField.include_in_all()); 29 | } 30 | else if (!tokenCountField.index()) { 31 | mappingBuilder.field("include_in_all", false); 32 | } 33 | 34 | if (!tokenCountField.index()) { 35 | mappingBuilder.field("index", tokenCountField.index()); 36 | } 37 | 38 | if (StringUtils.isNotBlank(tokenCountField.null_value())) { 39 | mappingBuilder.field("null_value", tokenCountField.null_value()); 40 | } 41 | 42 | if (tokenCountField.store()) { 43 | mappingBuilder.field("store", tokenCountField.store()); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/utils/BeanUtils.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.utils; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.ParameterizedType; 7 | import java.lang.reflect.Type; 8 | import java.util.List; 9 | import java.util.Set; 10 | 11 | public class BeanUtils { 12 | public static Field[] retrieveFields(Class clazz) { 13 | List fields = Lists.newArrayList(); 14 | Class targetClass = clazz; 15 | do { 16 | fields.addAll(Lists.newArrayList(targetClass.getDeclaredFields())); 17 | targetClass = targetClass.getSuperclass(); 18 | } 19 | while (targetClass != null && targetClass != Object.class); 20 | 21 | return fields.toArray(new Field[fields.size()]); 22 | } 23 | 24 | public static boolean isCollectionType(Field field) { 25 | return field.getType() == List.class 26 | || field.getType() == Set.class 27 | || field.getType().isArray(); 28 | } 29 | 30 | public static boolean isValidCollectionType(Field field) { 31 | if (field.getType().isArray()) { 32 | return true; 33 | } 34 | 35 | if (isCollectionType(field)) { 36 | Type[] argTypes = ((ParameterizedType) field.getGenericType()).getActualTypeArguments(); 37 | return argTypes.length == 1; 38 | } 39 | return false; 40 | 41 | } 42 | 43 | public static Type getCollectionGenericType(Field field) { 44 | if (field.getType().isArray()) { 45 | return ((Class) field.getGenericType()).getComponentType(); 46 | } 47 | return ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/elasticsearch/mapper/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.utils; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class StringUtils { 7 | 8 | public final static String EMPTY = ""; 9 | 10 | public static boolean equals(Object arg1, Object arg2) { 11 | if (arg1 == null || arg2 == null) { 12 | return false; 13 | } 14 | return arg1.toString().equals(arg2.toString()); 15 | } 16 | 17 | 18 | public static boolean isBlank(String str) { 19 | int strLen; 20 | if (str == null || (strLen = str.length()) == 0) { 21 | return true; 22 | } 23 | for (int i = 0; i < strLen; i++) { 24 | if (!Character.isWhitespace(str.charAt(i))) { 25 | return false; 26 | } 27 | } 28 | return true; 29 | } 30 | 31 | public static boolean isNotBlank(String str) { 32 | return !StringUtils.isBlank(str); 33 | } 34 | 35 | public static boolean isEmpty(String str) { 36 | return str == null || str.length() == 0; 37 | } 38 | 39 | public static Boolean isNumeric(String... params) { 40 | Pattern pattern = Pattern.compile("^[0-9]+$"); 41 | for (String param : params) { 42 | if (StringUtils.isEmpty(param)) { 43 | return false; 44 | } 45 | Matcher matcher = pattern.matcher(param); 46 | if (!matcher.matches()) { 47 | return false; 48 | } 49 | } 50 | return true; 51 | } 52 | 53 | public static boolean isAllBlank(String... params) { 54 | for (String param : params) { 55 | if (!StringUtils.isBlank(param)) { 56 | return false; 57 | } 58 | } 59 | return true; 60 | } 61 | 62 | public static boolean isAllNotBlank(String... params) { 63 | for (String param : params) { 64 | if (StringUtils.isBlank(param)) { 65 | return false; 66 | } 67 | } 68 | return true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | appender.console.type = Console 2 | appender.console.name = console 3 | appender.console.layout.type = PatternLayout 4 | 5 | rootLogger.level = info 6 | rootLogger.appenderRef.console.ref = console -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/Component.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | public class Component { 4 | 5 | private String serialNo; 6 | 7 | private String madeIn; 8 | } 9 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/Computer.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | import org.elasticsearch.mapper.annotations.Document; 4 | 5 | /** 6 | * I'm MacBook's father 7 | */ 8 | @Document(_type = "computer") 9 | public class Computer { 10 | private String parentField; 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/Cpu.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | public class Cpu extends Component { 4 | private int coreNumber; 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/MacBook.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | import org.elasticsearch.mapper.annotations.Document; 4 | import org.elasticsearch.mapper.annotations.enums.StringType; 5 | import org.elasticsearch.mapper.annotations.fieldtype.MultiField; 6 | import org.elasticsearch.mapper.annotations.fieldtype.MultiNestedField; 7 | import org.elasticsearch.mapper.annotations.fieldtype.StringField; 8 | import org.elasticsearch.mapper.annotations.fieldtype.TokenCountField; 9 | import org.elasticsearch.mapper.annotations.meta.MetaField_All; 10 | import org.elasticsearch.mapper.annotations.meta.MetaField_Parent; 11 | 12 | import java.util.List; 13 | 14 | @Document(_type = "macBook", _all = @MetaField_All(enabled = false), _parent = @MetaField_Parent(parentClass = Computer.class)) 15 | public class MacBook extends Component { 16 | 17 | // default: keyword 18 | private String deviceName; 19 | 20 | @StringField(type = StringType.Text) 21 | private String manufacturer; 22 | 23 | @MultiField( 24 | mainField = @StringField(type = StringType.Keyword, boost = 2.0f), 25 | fields = { 26 | @MultiNestedField(name = "pinyin", field = @StringField(type = StringType.Text, analyzer = "lc_pinyin")), 27 | @MultiNestedField(name = "cn", field = @StringField(type = StringType.Text, analyzer = "ik_smart")), 28 | @MultiNestedField(name = "en", field = @StringField(type = StringType.Text, analyzer = "english")), 29 | }, 30 | tokenFields = { 31 | @TokenCountField(name = "cnTokenCount", analyzer = "ik_smart") 32 | } 33 | ) 34 | private String introduction; 35 | 36 | // nested doc 37 | private List users; 38 | 39 | // inner doc 40 | private Cpu cpu; 41 | 42 | //inner doc 43 | private Memory memory; 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/Memory.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | 4 | import org.elasticsearch.mapper.annotations.enums.NumberType; 5 | import org.elasticsearch.mapper.annotations.fieldtype.NumberField; 6 | 7 | public class Memory extends Component { 8 | @NumberField(type = NumberType.Byte) 9 | private Byte memorySize; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/entity/User.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.entity; 2 | 3 | public class User { 4 | 5 | private boolean isRoot; 6 | 7 | private String username; 8 | 9 | private String password; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/org/elasticsearch/mapper/test/MappingTest.java: -------------------------------------------------------------------------------- 1 | package org.elasticsearch.mapper.test; 2 | 3 | import org.elasticsearch.mapper.entity.MacBook; 4 | import org.elasticsearch.mapper.mapper.MappingBuilder; 5 | import org.junit.Test; 6 | 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | public class MappingTest { 11 | @Test 12 | public void testGenMapping() throws IOException { 13 | MappingBuilder mappingBuilder = new MappingBuilder(); 14 | Map mappings = mappingBuilder.buildMappingAsString(MacBook.class); 15 | 16 | for (String typeName : mappings.keySet()) { 17 | System.out.println(mappings.get(typeName)); 18 | } 19 | } 20 | } 21 | --------------------------------------------------------------------------------