View Javadoc
1   package ca.uhn.fhir.jpa.entity;
2   
3   /*
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.model.api.IQueryParameterType;
24  import ca.uhn.fhir.rest.param.StringParam;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.commons.lang3.builder.EqualsBuilder;
27  import org.apache.commons.lang3.builder.HashCodeBuilder;
28  import org.apache.commons.lang3.builder.ToStringBuilder;
29  import org.apache.commons.lang3.builder.ToStringStyle;
30  import org.hibernate.search.annotations.*;
31  
32  import javax.persistence.*;
33  import javax.persistence.Index;
34  
35  import static org.apache.commons.lang3.StringUtils.left;
36  
37  //@formatter:off
38  @Embeddable
39  @Entity
40  @Table(name = "HFJ_SPIDX_STRING", indexes = {
41  	@Index(name = "IDX_SP_STRING", columnList = "RES_TYPE,SP_NAME,SP_VALUE_NORMALIZED"),
42  	@Index(name = "IDX_SP_STRING_UPDATED", columnList = "SP_UPDATED"),
43  	@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
44  })
45  @Indexed()
46  //@AnalyzerDefs({
47  //	@AnalyzerDef(name = "autocompleteEdgeAnalyzer",
48  //		tokenizer = @TokenizerDef(factory = PatternTokenizerFactory.class, params= {
49  //			@Parameter(name="pattern", value="(.*)"),
50  //			@Parameter(name="group", value="1")
51  //		}),
52  //		filters = {
53  //			@TokenFilterDef(factory = LowerCaseFilterFactory.class),
54  //			@TokenFilterDef(factory = StopFilterFactory.class),
55  //			@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = {
56  //				@Parameter(name = "minGramSize", value = "3"),
57  //				@Parameter(name = "maxGramSize", value = "50") 
58  //			}), 
59  //		}),
60  //	@AnalyzerDef(name = "autocompletePhoneticAnalyzer",
61  //		tokenizer = @TokenizerDef(factory=StandardTokenizerFactory.class),
62  //		filters = {
63  //			@TokenFilterDef(factory=StandardFilterFactory.class),
64  //			@TokenFilterDef(factory=StopFilterFactory.class),
65  //			@TokenFilterDef(factory=PhoneticFilterFactory.class, params = {
66  //				@Parameter(name="encoder", value="DoubleMetaphone")
67  //			}),
68  //			@TokenFilterDef(factory=SnowballPorterFilterFactory.class, params = {
69  //				@Parameter(name="language", value="English") 
70  //			})
71  //		}),
72  //	@AnalyzerDef(name = "autocompleteNGramAnalyzer",
73  //		tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
74  //		filters = {
75  //			@TokenFilterDef(factory = WordDelimiterFilterFactory.class),
76  //			@TokenFilterDef(factory = LowerCaseFilterFactory.class),
77  //			@TokenFilterDef(factory = NGramFilterFactory.class, params = {
78  //				@Parameter(name = "minGramSize", value = "3"),
79  //				@Parameter(name = "maxGramSize", value = "20") 
80  //			}),
81  //		}),
82  //	@AnalyzerDef(name = "standardAnalyzer",
83  //		tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
84  //		filters = {
85  //			@TokenFilterDef(factory = LowerCaseFilterFactory.class),
86  //		}) // Def
87  //	}
88  //)
89  //@formatter:on
90  public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam {
91  
92  	/*
93  	 * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here
94  	 */
95  	public static final int MAX_LENGTH = 200;
96  	public static final int HASH_PREFIX_LENGTH = 1;
97  	private static final long serialVersionUID = 1L;
98  	@Id
99  	@SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING")
100 	@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING")
101 	@Column(name = "SP_ID")
102 	private Long myId;
103 
104 	@ManyToOne(optional = false)
105 	@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_SPIDXSTR_RESOURCE"))
106 	@ContainedIn
107 	private ResourceTable myResourceTable;
108 
109 	@Column(name = "SP_VALUE_EXACT", length = MAX_LENGTH, nullable = true)
110 	@Fields({
111 		@Field(name = "myValueText", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
112 		@Field(name = "myValueTextEdgeNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteEdgeAnalyzer")),
113 		@Field(name = "myValueTextNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteNGramAnalyzer")),
114 		@Field(name = "myValueTextPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
115 	})
116 	private String myValueExact;
117 
118 	@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
119 	private String myValueNormalized;
120 	/**
121 	 * @since 3.4.0 - At some point this should be made not-null
122 	 */
123 	@Column(name = "HASH_NORM_PREFIX", nullable = true)
124 	private Long myHashNormalizedPrefix;
125 	/**
126 	 * @since 3.4.0 - At some point this should be made not-null
127 	 */
128 	@Column(name = "HASH_EXACT", nullable = true)
129 	private Long myHashExact;
130 
131 	public ResourceIndexedSearchParamString() {
132 		super();
133 	}
134 
135 
136 	public ResourceIndexedSearchParamString(String theName, String theValueNormalized, String theValueExact) {
137 		setParamName(theName);
138 		setValueNormalized(theValueNormalized);
139 		setValueExact(theValueExact);
140 	}
141 
142 	@PrePersist
143 	public void calculateHashes() {
144 		if (myHashNormalizedPrefix == null) {
145 			setHashNormalizedPrefix(hash(getResourceType(), getParamName(), left(getValueNormalized(), HASH_PREFIX_LENGTH)));
146 			setHashExact(hash(getResourceType(), getParamName(), getValueExact()));
147 		}
148 	}
149 
150 	@Override
151 	protected void clearHashes() {
152 		myHashNormalizedPrefix = null;
153 		myHashExact = null;
154 	}
155 
156 	@Override
157 	public boolean equals(Object theObj) {
158 		if (this == theObj) {
159 			return true;
160 		}
161 		if (theObj == null) {
162 			return false;
163 		}
164 		if (!(theObj instanceof ResourceIndexedSearchParamString)) {
165 			return false;
166 		}
167 		ResourceIndexedSearchParamString obj = (ResourceIndexedSearchParamString) theObj;
168 		EqualsBuilder b = new EqualsBuilder();
169 		b.append(getParamName(), obj.getParamName());
170 		b.append(getResource(), obj.getResource());
171 		b.append(getValueExact(), obj.getValueExact());
172 		b.append(getHashNormalizedPrefix(), obj.getHashNormalizedPrefix());
173 		b.append(getHashExact(), obj.getHashExact());
174 		return b.isEquals();
175 	}
176 
177 	public Long getHashExact() {
178 		calculateHashes();
179 		return myHashExact;
180 	}
181 
182 	public void setHashExact(Long theHashExact) {
183 		myHashExact = theHashExact;
184 	}
185 
186 	public Long getHashNormalizedPrefix() {
187 		calculateHashes();
188 		return myHashNormalizedPrefix;
189 	}
190 
191 	public void setHashNormalizedPrefix(Long theHashNormalizedPrefix) {
192 		myHashNormalizedPrefix = theHashNormalizedPrefix;
193 	}
194 
195 	@Override
196 	protected Long getId() {
197 		return myId;
198 	}
199 
200 	public String getValueExact() {
201 		return myValueExact;
202 	}
203 
204 	public void setValueExact(String theValueExact) {
205 		if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) {
206 			throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
207 		}
208 		myValueExact = theValueExact;
209 	}
210 
211 	public String getValueNormalized() {
212 		return myValueNormalized;
213 	}
214 
215 	public void setValueNormalized(String theValueNormalized) {
216 		if (StringUtils.defaultString(theValueNormalized).length() > MAX_LENGTH) {
217 			throw new IllegalArgumentException("Value is too long: " + theValueNormalized.length());
218 		}
219 		myValueNormalized = theValueNormalized;
220 	}
221 
222 	@Override
223 	public int hashCode() {
224 		HashCodeBuilder b = new HashCodeBuilder();
225 		b.append(getParamName());
226 		b.append(getResource());
227 		b.append(getValueExact());
228 		b.append(getHashNormalizedPrefix());
229 		b.append(getHashExact());
230 		return b.toHashCode();
231 	}
232 
233 	@Override
234 	public IQueryParameterType toQueryParameterType() {
235 		return new StringParam(getValueExact());
236 	}
237 
238 	@Override
239 	public String toString() {
240 		ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
241 		b.append("paramName", getParamName());
242 		b.append("resourceId", getResourcePid());
243 		b.append("value", getValueNormalized());
244 		return b.build();
245 	}
246 
247 }