From ad868038a86a8b3dfc14535db45170fcfeccca02 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 7 Oct 2015 09:01:32 -0400 Subject: [PATCH] Work on Lucene searching --- .../ca/uhn/fhir/rest/server/Constants.java | 2 + hapi-fhir-jpaserver-base/.gitignore | 1 + .../_0.cfe | Bin 299 -> 0 bytes .../_0.cfs | Bin 2081 -> 0 bytes .../_0.si | Bin 359 -> 0 bytes .../_1.cfe | Bin 299 -> 0 bytes .../_1.cfs | Bin 2040 -> 0 bytes .../_1.si | Bin 359 -> 0 bytes .../_2.cfe | Bin 299 -> 0 bytes .../_2.cfs | Bin 2081 -> 0 bytes .../_2.si | Bin 359 -> 0 bytes .../_3.cfe | Bin 299 -> 0 bytes .../_3.cfs | Bin 2040 -> 0 bytes .../_3.si | Bin 359 -> 0 bytes .../segments_5 | Bin 322 -> 0 bytes .../_0.cfe | Bin 299 -> 0 bytes .../_0.cfs | Bin 2022 -> 0 bytes .../_0.si | Bin 359 -> 0 bytes .../_1.cfe | Bin 299 -> 0 bytes .../_1.cfs | Bin 2026 -> 0 bytes .../_1.si | Bin 359 -> 0 bytes .../_2.cfe | Bin 299 -> 0 bytes .../_2.cfs | Bin 2022 -> 0 bytes .../_2.si | Bin 359 -> 0 bytes .../_3.cfe | Bin 299 -> 0 bytes .../_3.cfs | Bin 2026 -> 0 bytes .../_3.si | Bin 359 -> 0 bytes .../segments_5 | Bin 322 -> 0 bytes .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 61 ++++--- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 60 +++++-- .../ca/uhn/fhir/jpa/dao/FhirSearchDao.java | 98 +++++++++++ .../ca/uhn/fhir/jpa/dao/IFhirResourceDao.java | 4 +- .../java/ca/uhn/fhir/jpa/dao/ISearchDao.java | 6 +- .../java/ca/uhn/fhir/jpa/dao/SearchDao.java | 53 ------ .../ResourceIndexedSearchParamCoords.java | 1 - .../ResourceIndexedSearchParamDate.java | 1 - .../ResourceIndexedSearchParamNumber.java | 7 +- .../ResourceIndexedSearchParamQuantity.java | 7 +- .../ResourceIndexedSearchParamString.java | 1 - .../ResourceIndexedSearchParamToken.java | 1 - .../entity/ResourceIndexedSearchParamUri.java | 1 - .../ca/uhn/fhir/jpa/entity/ResourceTable.java | 27 +++ .../search/IndexNonDeletedInterceptor.java | 38 +++++ .../util/BigDecimalNumericFieldBridge.java | 42 +++++ .../fhir-spring-search-config-dstu2.xml | 2 +- .../ca/uhn/fhir/jpa/dao/BaseJpaDstu2Test.java | 2 +- ... => FhirResourceDaoDstu2SearchFtTest.java} | 4 +- .../FhirResourceDaoDstu2SearchNoFtTest.java | 53 ++++++ .../fhir/jpa/dao/FhirSearchDaoDstu2Test.java | 157 +++++++++++++++++- .../resources/vm/jpa_resource_provider.vm | 10 ++ 50 files changed, 529 insertions(+), 110 deletions(-) delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_0.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_0.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_0.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/segments_5 delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_2.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_2.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_2.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.cfe delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.cfs delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.si delete mode 100644 hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/segments_5 create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/BigDecimalNumericFieldBridge.java rename hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/{FhirResourceDaoDstu2SearchTest.java => FhirResourceDaoDstu2SearchFtTest.java} (99%) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchNoFtTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 5f30a89b7e5..db8d9c28949 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -147,6 +147,8 @@ public class Constants { public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue"; public static final String URL_TOKEN_HISTORY = "_history"; public static final String URL_TOKEN_METADATA = "metadata"; + public static final String PARAM_CONTENT = "_content"; + public static final String PARAM_TEXT = "_text"; static { Map valToEncoding = new HashMap(); diff --git a/hapi-fhir-jpaserver-base/.gitignore b/hapi-fhir-jpaserver-base/.gitignore index eaa09ffd910..4532b9d8844 100644 --- a/hapi-fhir-jpaserver-base/.gitignore +++ b/hapi-fhir-jpaserver-base/.gitignore @@ -1,6 +1,7 @@ target/ /bin nohup.out +lucene_indexes/ # Created by https://www.gitignore.io diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_0.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_0.cfe deleted file mode 100644 index cfffd3c842fd418f98a3fcbbcaa497b554281a62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1hV&%S6r=s_h-IoR?nV>r@{=J z{P8g5@dkPY`CwH*g?b>>K#JGE$<&~vCILviW zL2IB%DHTw`olwCL6q8c&lOf_PyioZT6#0@&s7qKTLgbkWK{i1J*_oi4l0Y_5s1Sk8)OrvC$i8_ub5?G0^DsR3#{JrraIs16I@}o@{W!9aZ^^|E-oy^!bES+MD z^=i2^c-HxTK3%_j?0vTG&8|-`M0lvE=bYAYQO%Ho8w2b~OK!<)`Wj89?9UBSyZZCVDRiGuqHc|q!#+2uk(p9lSt;bc^B2n!!EoU7P2%a($3P6MtawzYR0jUDSe-qjsH(ZjH2 zB`AyHR!f#yL-yxjPM0@$4$+;bI<6~}#oq5w-Sg}6;P7<5bu+ol_)GE?Al^$UC{fl( zqM+$kVzz`5m{6TM2~V3Ys94QnWGR}<^ena51qcHIynP|k2?oF*7y?N!3{p*iA?!U- zb&P?|dXVu=0FnUYa4JwhhF)~!8T~#c5@DJNl{AxJsw*Mx2-(x1W|%Sag-~n_mJroh zN<5r)MaB|B$UaN4O>H+L+fAm#6*#9;jhKY+`>m)yz-nIm^uadtVX4r7)lP_uX>_FetH~a4&y? z7$;oE4h>FViqv|)K&I-gvLJ4^r0o{}$$j0n9wgS;BzRgzc3Bf^;}G7|zRu;UI`c}G6`lkaoQ<|KbmuhMdoSL4SnpfhPmzK}Kz`!VbFL}k)`geckn`ZUwX?Q9O6k}io zVrCEn2-q08I7>2fQ;SOya|?J4O-xJ-EG*3pjSY>A`Lhzs67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#T)1)rxin}R8}CbIFnHx9!@3sMX4!nnW;G`#cugU kxrrrux%nxnTu!dRA@RWhuC6WzG$y}*1QG*-+&slx0I(o!_y7O^ diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfe deleted file mode 100644 index ed29c3e23b645ba4dbd5739b5a09d75d3755a971..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1hV&%S6r=s_h-IoR?nV>r{WBp zEP8omxgaSZ(1TD6Ni2G4DHTx81SlQC9}hD)-axM;6Q=12M4qV#q^ShLVGe_8nvbF> zB|jM|{}n1<4zfN4!ePmQ3R$}fk?9|qc#2eqCx3nIv*grcb+AM9NQ2KFAP RrkDd7lV3pD493=ZDF8n}Jahm6 diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfs b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.cfs deleted file mode 100644 index 93250da550b34ae562115f75e5dc7427c4c516f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2040 zcmcIl&1(}u6yKf6S6lntT5B7lh+t8cq*@9>sMrz++8Rty^k5iwC*7_45qBpwQNjEH ziU$uK)q~)vP%j=-=s`SqtVlh1C_tCmf;jgk(6pJfaS?KYj=+KCt~ z2RX1?hv5K5iy?ljrM{ju646SS%e`E^9gAo#!e8tp>7D3u3P731{EQ3E-@nKiH|SAk zGmF={Yb%MXRS^jx0SU=fN!V@8B2cTOFceE9g%g=+I1P&%8sg*9yAB>+zZc#{W=~i$ zPpJW~(a~e*cpw-GN1B>j4z#wlcXW1j_w*j@JJf%8U@&?lCSqLs_^XnA5&gnpJJ(=q z)1&U7@tY>+-+bbx-5Xoj8wQPfn495LHU}92i!ok?i6pv>?UM$#m4+6#(+#g|CymUx z($Pt|@&ww>U9bEpynH+Rm&elO72Ta_X|rHCpuCj{!H&GD-J-Th7|qK%aU219kq=D1 zP&7kr*MoO=Dc-kCD!5z31F5IxlnMo{EXD&yC9S}W*mK8iM#YsiaV31V8jOY!M~p&t(QNiIYa8SC0sOTRfD#4N%#N9@0+DgfAF8J Hh1iXM!7ivG diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_1.si deleted file mode 100644 index 60c08eb6ba0fc79e4881eea10428640f5d7540cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`!VbFL}k)`geckn`ZUwX?Q9Q6k}io zVrCEn2$&hUI7>2fQ;SOya|?J4O-xJ-EG*3pjSbCA__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#vAGtXR-mwOF k+{6;S-29YOE+^OEkoe#LS67z<8k1i@0*QfP)nu<+0J$V?s{jB1 diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfe deleted file mode 100644 index 82e3e0d70510dc0b252276666ed10961ce5ce57c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1n~vO&aS#}Q;5N1^IiKReq{`t z{P8g5@dkP+`N<$vK%fVq7+P5L^2&0doJ~+V2}M&uKGZ!l|Ky9Q~}{IYe5A=&@@4v$6N>1TnN;Z2Nh&}4;558 PpfULc)BuK-PkFNeDXTmu diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfs b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.cfs deleted file mode 100644 index ce2f91e807481d124f1fb9666b73176a96c4277e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2081 zcmcIl%WD%s7@wKl&7<$6t!?c~(L)a!YF`1Z6l@7bYz-z_@L(8sC(Y_U(%nhzsz}cY zRX~xZK;%46Q*2A zrN)!a_M=0G(GgJ!1ViDP+PeCN#->QLxux|$TYJaB&aUpBUMTPZ!$E1|O=24%{J!t; zZ*G|n34VUgV`wc|9h@>P-JT>AE7d|q8=3&z5jNk%&9`+n8vP#rEIu)J$dcvEb+-cJ z%hEGldG)(mnhxIMt|Dd0RK?z}s-9}^;i_F8x@l@!L`kSbQ|tt`X~_!Q8Yoy;fC$t9 z1V-17gSd=i#k>*A~2es0svh-UEcu* z;2<1=!*B$~>mbX{d!ouXOPK8tD;R(xfKoUf$YEX89BD?KBZg>TorDT`(;!rpLtGKD zr$$LK6|RVcNU(sZ%Gk)=dvfFN28@e40$Zxs+) z3G<8p&whi%koZY_;_10ZF5x`pxa}fogRjWt;**j1C)-?_KPTx^o$8 oMZ*6#H9`re++O>r)g8Cr2qf` diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_2.si deleted file mode 100644 index ac42b2c309c6e507e988407a9b2324e9529e6b62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`z(^aO~`=3pa%rJT~98KjK#g6k}io zVrCEn2-q08I7>2fQ;SOya|?J4O-xJ-EG*3}Oe{?;__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#vADsXR-mwOF k+{6;S-29YOE+^OEkoe#LS67z<8k1i@0*Qg)+S9aS0J^$v*8l(j diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfe deleted file mode 100644 index f345407f8fe26cf10f79b0baf900bc8912daa15b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1n~vO&aS#}Q;5N1^IiKRepL*d zEP81v6(A`f(1TD6A^h<$GvW>ON-}ey;!O~msfa}{uPg=1`2v-+M$=RP5ogwi${$9N zFUSYGhk=3l6I4D1=#o6BO)P#8K_(@TO;ABLsEG_oAUBslRIpuwYM+l{d`f;YM4Y`4 SDqns;WAY0qn_&X0>@@)XzdN-6 diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfs b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.cfs deleted file mode 100644 index 73b409208e5865a12034d7614b3167663796d925..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2040 zcmcIl&ubGw6rP<;vT3VLo3z&cvWgTeLP#p5UerLZeEQ2ib9`?P9)FN2PX;@dB9CB#h zRB(+&35UXmoPD$Sxu(wEj$R+?o*dj z-c|s|7@mQKW>?KoP(P87TqNDVR>f^ms&IsR^JB-C%jL5tPQli`o-xhD>DOsI6Ki zrkdPvn_+H{d5YbVji|ukO(&-d|FSvku?OmfT>Ymgz_loUah#-&VUGw;(Tw z1FPYttC5XnzS2&!)##eHIyo=9FIla5_CA;bUD~|GS^Jw84l5%b)1qwZn2p~c_C!q< zICk8}_Bun}tazY@n?ks&tZEN2F%iXOSx1fon4)+03=S7C@eusl&T;kAWW{?#eUhWx zoNA?_)r3^YaPx|A3c#I)>-UM0Yhwuh|K4MUdl%_d%-gb#DHczGqr-5$&bGiBRp=D( zg9>@^r?$!!(z~zr<}pReQ5O!=bKRbajZvsE;#!FX84~ZTDb6tD%j4i3wO8ne)wr_4 zI&uU+kl9UwlXk=?>x3CFHj>VO(ZV8|jSKs(yDx92;w{Q=){=Q@Ey@pGyaX?Y!VxLj r*4}aEY-g;iJD%w2J=b@>e_(KE_`(S5E%i_ERMxjAKyW_r@%fK`V|kmu diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/_3.si deleted file mode 100644 index 4d622db680311c2adee3a0fb5613605b94197c50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`z(^aO~`=3pa%rJT~98KjK#f6k}io zVrCEn2$&hUI7>2fQ;SOya|?J4O-xJ-EG*3}ObrZ7__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#T)A-rxmjTsp3osCzVki9!@3sMX4!nnW;G`#cugU kxrrrux%nxnTu!dRA@RWhuC6WzG$y}*1QG*7N$R>q0I1Y%3;+NC diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/segments_5 b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString/segments_5 deleted file mode 100644 index 1bce37ddb622663ec4ce0604fcd2010abd75e9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 322 zcmcD&o+HjtoSL4SnpaZHz`(#3UvTW~stY%T7(6!LwLjul&1lNX3={_gJ|M{g!ayOW zcmqb+d&w)V*1!8R-!!XdPs3AT4xiHG)Vx$v}F`~J% bj1=9*=(?+j*UfN1WAY2IAArQ)ob7=CRnDUy diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfe deleted file mode 100644 index 60a9401621cab40b64f8614b97fdb3df098e6926..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1hV&%S6r=s_h-IoR?nV>r-BTe z{P8g5@dkPY`CwH*g?b>>KoHELmzI|cVKX#BXr^)$O(mHHQ1Ks7b+=jc^2$;m9OlbV zL2IB%DHRY73m;T47{#QN{A8$nB2+#PMZP2x>Jpao5P2p(kWElQw!2VGIUt)#ARKm2 Ss9^5_jma;dYzFT*p-cdmw>&HW diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfs b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.cfs deleted file mode 100644 index a26fbaa7377214b0aae3f543101458c793c7f01f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2022 zcmcIlOK1~87@kKqNn353KCHEg54?Dg5Rq!B)oLxlmbMfV^rUs%otSO2yI~*DMZpLj zwI1{?6bqj8;7tXkdhz5*y!8+edMeVJBIwK{Tc>SOp_qkb|M};C&-cv^e=YQMO?WCb zX@BaXWfU#X)JBO*0DBTE>hmvm*M2?ipMCJQ^eqO4nRRbqJ#9IzZr*aP+PX_^wyNdQ z99Yk!_%;CVk3C+kNC1rgSS|999f46=rv+`y%!ycVTl#SOOkTf9ZIifkMlBG>0SJTN z)dwY}sVyP|m8C7)AZ{`zKG7gaJu}D4<@9-&3K{;G1{)AKYWNx7ajAj8|F62$@Gn!x z@@$o6{UXH;NYP^W)5Ipm6ftP10r4Y-e}xo0sx%zCGyT@wLMGOlG)O%TdS6UPnavz?cS)dImF zd%Vs(}(hB Kc|CFMMdS}k$)Py_ diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_0.si deleted file mode 100644 index e5f8ebbc4728826712b9c0999fcb40a3d020aabc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`!VbFL}k)`geckn`ZUwX?Q9K6k}io zVrCEn2pAc;I7>2fQ;SOya|?J4O-xJ-EG*3pjSUS<__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#T)1)rxin}R8}CbIFnHx9!@3sMX4!nnW;G`#cugU kxrrrux%nxnTu!dRA@RWhuC6WzG$y}*1QG*-!35bJ0H*V94*&oF diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.cfe deleted file mode 100644 index 9dc809bc4b80e42002515f0925f423c2e89f55f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1hV&%S6r=s_h-IoR?nV>r=kp; zEP8omxgaSZ(1TD6IV^fY~nz#h;mH4W?{`~mI?-!$sPdCcny1@kZ?%wepnX#GaOjDQWwcL>a z5o-lk-h3C?-6^}fKYX>mJ^%cBV0r)H$vu%P5mc6SZASHsL5sSRA&xs`>2#TU!viHE z$gNh^T5SK+fEQs-i(wY#&Kyb7BHMY|Q2PIZ6Lj({S|9Jr%@;AgdcL>APe4?B0L^XXu zHG6fIwl%?^j>^#BDRd^HMAcYRy!pt{me#iRj?S*`V?Din$NL8oCr*a5jrYE471`Q% zvQ>JyX4{8DucQ}#iO|lyeH1igy+#sYIFrqBA_{;QZ}Is6Wp{yugwVH3Vs;|0M z%~#!)*2~kH{|vt5(?PEFFtxeO;aj4{j^GaDyqU7{w!@td>LA)SUoaLZvxrOQwIXpG zfG`llOe~a`rA#6OSDE1hleo#ee8qw!wcH#ppEs6aDq{LmI&1)N%=ELB0RjGh)h(tU z%(k3gqPPJmnoVC$${AwPNCWUy)4xNC9#tBE9hTi-^Tm%sb~Rd`t4Bz+PU}l0gD~U+ N^U<5|`P|0(>A&sJp6LJp diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_1.si deleted file mode 100644 index 5da78ffc63092051a00109e8f52d1b99d709689f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`!VbFL}k)`geckn`ZUwX?Q9M6k}io zVrCEn2pAc;I7>2fQ;SOya|?J4O-xJ-EG*3pjSWo=__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#vAGtXR-mwOF k+{6;S-29YOE+^OEkoe#LS67z<8k1i@0*QfP-ke)*0I%e42mk;8 diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_2.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_2.cfe deleted file mode 100644 index 91ea7c3833b88273a94c80f1a74cd34217608266..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1n~vO&aS#}Q;5N1^IiKRekBZ? z{P8g5@dkP+`N<$vK%fVq81h*3^2&0doHA7&cBQ-9>`oI_J#h1e z2M>mm3DJue4<0<27!QPa(Qwk3C{Yf^cryG0I5TB&fEE*CC)w}5Y2p3z#n0{F!ppD8`7{Gb9^Df}PdFYk?c3fp*JRWk zb@XZ}jX`uSfesu*10gLOi8eGg?b_Yk(%RPE(b=`9dvDLa-oDuW{r$Gf{2XLDXN@-Zpok*Ua4M7KVH$uKFZ1((a)K1ER@EDxsTLbvs^*Q%RrRh& z@5yo9e+Zw!$^n7bDz%G+^N#2-R!irAa)sr(yQTgBNldf@77x@R*&K_sQ0&3b*D8 z<}K>lgwdQ{B%TKl2I9673&kd=D-nX}Nbwtyuy|g+qCt^bVO}|(H>Y4CWcgzTtOIb= z@-vlT0RO-1X3Gz*uB>08xDF+nEMH!PX=2e(9q=QTf0Y!=RI3BFI(CoQ<|KbmuhMdoSL4SnpfhPmzK}Kz`z(^aO~`=3pa%rJT~98KjK#c6k}io zVrCEn2pAc;I7>2fQ;SOya|?J4O-xJ-EG*3}Oe~Eo`Lhzs67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#vADsXR-mwOF k+{6;S-29YOE+^OEkoe#LS67z<8k1i@0*Qek;ko`g0IlwB3jhEB diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.cfe b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.cfe deleted file mode 100644 index 1eeedddb0d91bd7429168f5e9a8aa7ac98762889..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmcD&o+B>qQ<|KbmuhO@oS$2eUz(TVnpaYknOe*M1n~vO&aS#}Q;5N1^IiKReiaOy zEP81v6(A`f(1TD6!Tj+sGvW>ON-}ey;*AiRNsvV^uPg=1nFN)zM$=RPl|KuWzl|ba zkPmhb0|WC4sC+QcC3#Srn14exmxFAA3bGYJRpx-)Tms>+-Gd7Dp%|Z%p9~Ra&wIk?uOl2>_QJc zc<>@ZJ&2yQ2f>4Qs~{DM;6d;vJ$MNSdK1B3b!OVF+cwRin1Ny6ym{|??=jzdoBmSn z?!4}4)S_onS8TIldzL;<7y*10Djz<*{JfR`*WP?huTIT@G`H>#taHqEsGc!sS$8wU zWfPW8=eQT#<%S^Ff)T#K{L0|t?*dz_nfbLr0ht&p+uCg2p)^k&(;YW7#&6M1oOlqq zkdm472Is8pGQ+y#&Nv36j>Hh~b^I3~w$ExL>6NTxv1B;OyA_h87BiTioTjerIU1Gq zP(4S9qm{D6A!gp5r4}y+-?|8}iiU=zdUWj!(pH=N1kc{&P!7*~;?>kh3_QyX2ZWgD$&t1i{DRoA8V+@$6|f-iW7$n`c{ z+UUb?Pee97wfWB3FMR5-eFHNL`TpkfcLFjIK*~0ru!<6EcLzt?3MJz~zpI8uNFEiIG7L7TWikSX{4jTX*GyU8~-+}+1b&Kf- zBO~uuC~iQAX497gXo{FL(g1wb^ly-|N0kO(hiR8)E6aj*UEBJ5k}{(2)#JQ7Hr71e zk|JA@vLy*BwuWP!e=u-W#Oe#!xAT9sIaYR)ePID2fY61Xr@%f(YK+fO2*cUD#;ZT- z^zgW?iAw79Fd8_HPDGTb8f%I-A2`_3+SZ=v=W8rA4Lr=5e^YY1O Gt$zWTX_$=w diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.si b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/_3.si deleted file mode 100644 index 39d782d857d44062bde77bb96df839d8c2bc3593..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 359 zcmcD&o+B>oQ<|KbmuhMdoSL4SnpfhPmzK}Kz`z(^aO~`=3pa%rJT~98KjK#b6k}io zVrCEn2pAc;I7>2fQ;SOya|?J4O-xJ-EG*3}Oe`(T__Gqr67|Y*^~zFKW@9uodT*7A2>$rsb3t zXRrgcCl)1VuvJ)?#haOkfbA(N%>%gurj5r?&s@(S9%P@PB@e_U5NB|}WVoTCc`5lt zLjFaG$vLSC&iO?J`9+B(K<6^E#T)A-rxmjTsp3osCzVki9!@3sMX4!nnW;G`#cugU kxrrrux%nxnTu!dRA@RWhuC6WzG$y}*1QG)S(|M);0JaKk;{X5v diff --git a/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/segments_5 b/hapi-fhir-jpaserver-base/lucene_indexes/ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken/segments_5 deleted file mode 100644 index 7a0781a26f9c8b68896a30e14ab057d0e6c0eea9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 322 zcmcD&o+HjtoSL4SnpaZHz`(#3UvTW~stY%T7(6!LwLjul$!N;T3={_gJ|M{g!ayOW zcmqb+d&w)V*1!8R-!!XdPs39|4xiHG)Vx$v implements IDao { try { resourceDefinition = getContext().getResourceDefinition(typeString); } catch (DataFormatException e) { - throw new InvalidRequestException( - "Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue()); + throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue()); } Class type = resourceDefinition.getImplementingClass(); @@ -287,8 +288,7 @@ public abstract class BaseHapiFhirDao implements IDao { } if (!typeString.equals(target.getResourceType())) { - throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart() - + " is actually of type " + target.getResourceType()); + throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart() + " is actually of type " + target.getResourceType()); } /* @@ -716,9 +716,11 @@ public abstract class BaseHapiFhirDao implements IDao { } /** - * This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds. + * This method is called when an update to an existing resource detects that the resource supplied for update is + * missing a tag/profile/security label that the currently persisted resource holds. *

- * The default implementation removes any profile declarations, but leaves tags and security labels in place. Subclasses may choose to override and change this behaviour. + * The default implementation removes any profile declarations, but leaves tags and security labels in place. + * Subclasses may choose to override and change this behaviour. *

* * @param theEntity @@ -726,7 +728,8 @@ public abstract class BaseHapiFhirDao implements IDao { * @param theTag * The tag * @return Retturns true if the tag should be removed - * @see Updates to Tags, Profiles, and Security Labels for a description of the logic that the default behaviour folows. + * @see Updates to Tags, Profiles, and Security + * Labels for a description of the logic that the default behaviour folows. */ protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) { if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) { @@ -763,13 +766,13 @@ public abstract class BaseHapiFhirDao implements IDao { RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType); SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef); - + if (paramMap.isEmpty()) { throw new InvalidRequestException("Invalid match URL[" + theMatchUrl + "] - URL has no search parameters"); } IFhirResourceDao dao = getDao(theResourceType); - Set ids = dao.searchForIdsWithAndOr(paramMap); + Set ids = dao.searchForIdsWithAndOr(paramMap, new HashSet()); return ids; } @@ -845,7 +848,7 @@ public abstract class BaseHapiFhirDao implements IDao { if (RESOURCE_META_PARAMS.containsKey(nextParamName)) { if (isNotBlank(paramList.get(0).getQualifier()) && paramList.get(0).getQualifier().startsWith(".")) { - throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier()); + throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier()); } IQueryParameterAnd type = newInstanceAnd(nextParamName); type.setValuesAsQueryTokens((paramList)); @@ -857,7 +860,7 @@ public abstract class BaseHapiFhirDao implements IDao { if (paramDef == null) { throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName); } - + IQueryParameterAnd param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList); paramMap.add(nextParamName, param); } @@ -1100,7 +1103,8 @@ public abstract class BaseHapiFhirDao implements IDao { } } else if (theForHistoryOperation) { /* - * If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT. + * If the create and update times match, this was when the resource was created so we should mark it as a POST. + * Otherwise, it's a PUT. */ Date published = theEntity.getPublished().getValue(); Date updated = theEntity.getUpdated().getValue(); @@ -1194,8 +1198,7 @@ public abstract class BaseHapiFhirDao implements IDao { } @SuppressWarnings("unchecked") - protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime) { + protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) { /* * This should be the very first thing.. @@ -1204,8 +1207,7 @@ public abstract class BaseHapiFhirDao implements IDao { validateResourceForStorage((T) theResource, theEntity); String resourceType = myContext.getResourceDefinition(theResource).getName(); if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) { - throw new UnprocessableEntityException( - "Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]"); + throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]"); } } @@ -1261,6 +1263,7 @@ public abstract class BaseHapiFhirDao implements IDao { links = Collections.emptySet(); theEntity.setDeleted(theDeletedTimestampOrNull); theEntity.setUpdated(theDeletedTimestampOrNull); + theEntity.setNarrativeTextParsedIntoWords(null); } else { @@ -1309,6 +1312,7 @@ public abstract class BaseHapiFhirDao implements IDao { theEntity.setResourceLinks(links); theEntity.setHasLinks(links.isEmpty() == false); theEntity.setIndexStatus(INDEX_STATUS_INDEXED); + theEntity.setNarrativeTextParsedIntoWords(parseNarrativeTextIntoWords(theResource)); } else { @@ -1425,7 +1429,8 @@ public abstract class BaseHapiFhirDao implements IDao { } /** - * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time. + * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the + * first time. * * @param theEntity * The resource @@ -1437,7 +1442,8 @@ public abstract class BaseHapiFhirDao implements IDao { } /** - * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time. + * Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the + * first time. * * @param theEntity * The resource @@ -1449,8 +1455,9 @@ public abstract class BaseHapiFhirDao implements IDao { } /** - * This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the - * "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check. + * This method is invoked immediately before storing a new resource, or an update to an existing resource to allow + * the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects + * resources which have it. Subclasses should call the superclass implementation to preserve this check. * * @param theResource * The resource that is about to be persisted @@ -1481,4 +1488,18 @@ public abstract class BaseHapiFhirDao implements IDao { return new String(out).toUpperCase(); } + private static String parseNarrativeTextIntoWords(IResource theResource) { + StringBuilder b = new StringBuilder(); + List xmlEvents = theResource.getText().getDiv().getValue(); + if (xmlEvents != null) { + for (XMLEvent next : xmlEvents) { + if (next.isCharacters()) { + Characters characters = next.asCharacters(); + b.append(characters.getData()).append(" "); + } + } + } + return b.toString(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 3cc97b102b8..ab66be400a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -158,6 +158,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH @Autowired private DaoConfig myDaoConfig; + @Autowired(required=false) + private ISearchDao mySearchDao; + private String myResourceName; private Class myResourceType; private String mySecondaryPrimaryKeyParamName; @@ -2147,7 +2150,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH StopWatch w = new StopWatch(); final InstantDt now = InstantDt.withCurrentTime(); - Set loadPids; + Collection loadPids; if (theParams.getEverythingMode() != null) { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); @@ -2185,12 +2188,24 @@ public abstract class BaseHapiFhirResourceDao extends BaseH if (loadPids.isEmpty()) { return new SimpleBundleProvider(); } - } else { - loadPids = searchForIdsWithAndOr(theParams); + } else { + + List searchResultPids; + if (mySearchDao == null) { + searchResultPids = null; + } else { + searchResultPids = mySearchDao.search(getResourceName(), theParams); + } + if (theParams.isEmpty()) { + loadPids = searchResultPids; + } else { + loadPids = searchForIdsWithAndOr(theParams, searchResultPids); + } if (loadPids.isEmpty()) { return new SimpleBundleProvider(); } + } // // Load _include and _revinclude before filter and sort in everything mode @@ -2279,15 +2294,15 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return retVal; } - private List filterResourceIdsByLastUpdated(Set loadPids, final DateRangeParam lu) { + private List filterResourceIdsByLastUpdated(Collection thePids, final DateRangeParam theLastUpdated) { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery(Long.class); Root from = cq.from(ResourceTable.class); cq.select(from.get("myId").as(Long.class)); - Predicate predicateIds = (from.get("myId").in(loadPids)); - Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from. get("myUpdated"), lu.getLowerBoundAsInstant()) : null; - Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from. get("myUpdated"), lu.getUpperBoundAsInstant()) : null; + Predicate predicateIds = (from.get("myId").in(thePids)); + Predicate predicateLower = theLastUpdated.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()) : null; + Predicate predicateUpper = theLastUpdated.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from. get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()) : null; if (predicateLower != null && predicateUpper != null) { cq.where(predicateIds, predicateLower, predicateUpper); } else if (predicateLower != null) { @@ -2313,20 +2328,20 @@ public abstract class BaseHapiFhirResourceDao extends BaseH return query; } - private List processSort(final SearchParameterMap theParams, Set theLoadPids) { + private List processSort(final SearchParameterMap theParams, Collection theLoadPids) { final List pids; - Set loadPids = theLoadPids; +// Set loadPids = theLoadPids; if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) { List orders = new ArrayList(); List predicates = new ArrayList(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createTupleQuery(); Root from = cq.from(ResourceTable.class); - predicates.add(from.get("myId").in(loadPids)); + predicates.add(from.get("myId").in(theLoadPids)); createSort(builder, from, theParams.getSort(), orders, predicates); if (orders.size() > 0) { - Set originalPids = loadPids; - loadPids = new LinkedHashSet(); + Collection originalPids = theLoadPids; + LinkedHashSet loadPids = new LinkedHashSet(); cq.multiselect(from.get("myId").as(Long.class)); cq.where(predicates.toArray(new Predicate[0])); cq.orderBy(orders); @@ -2349,10 +2364,20 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } } else { - pids = new ArrayList(loadPids); + pids = toList(theLoadPids); } } else { - pids = new ArrayList(loadPids); + pids = toList(theLoadPids); + } + return pids; + } + + private List toList(Collection theLoadPids) { + final List pids; + if (theLoadPids instanceof List) { + pids = (List) theLoadPids; + } else { + pids = new ArrayList(theLoadPids); } return pids; } @@ -2368,7 +2393,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH for (Entry nextEntry : theParams.entrySet()) { map.add(nextEntry.getKey(), (nextEntry.getValue())); } - return searchForIdsWithAndOr(map); + return searchForIdsWithAndOr(map, null); } @Override @@ -2377,7 +2402,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } @Override - public Set searchForIdsWithAndOr(SearchParameterMap theParams) { + public Set searchForIdsWithAndOr(SearchParameterMap theParams, Collection theInitialPids) { SearchParameterMap params = theParams; if (params == null) { params = new SearchParameterMap(); @@ -2386,6 +2411,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceType); Set pids = new HashSet(); + if (theInitialPids != null) { + pids.addAll(theInitialPids); + } for (Entry>> nextParamEntry : params.entrySet()) { String nextParamName = nextParamEntry.getKey(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java new file mode 100644 index 00000000000..eb60e1dec87 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSearchDao.java @@ -0,0 +1,98 @@ +package ca.uhn.fhir.jpa.dao; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceContextType; + +import org.apache.commons.lang3.StringUtils; +import org.apache.lucene.search.Query; +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.FullTextQuery; +import org.hibernate.search.query.dsl.BooleanJunction; +import org.hibernate.search.query.dsl.QueryBuilder; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.transaction.annotation.Transactional; + +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.server.Constants; + +public class FhirSearchDao extends BaseHapiFhirDao implements ISearchDao { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSearchDao.class); + + @PersistenceContext(type = PersistenceContextType.TRANSACTION) + private EntityManager myEntityManager; + + @Transactional() + @Override + public List search(String theResourceName, SearchParameterMap theParams) { + FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); + + QueryBuilder qb = em + .getSearchFactory() + .buildQueryBuilder() + .forEntity(ResourceTable.class).get(); + + BooleanJunction bool = qb.bool(); + + List> contentAndTerms = theParams.remove(Constants.PARAM_CONTENT); + addTextSearch(qb, bool, contentAndTerms, "myParamsString.myValueComplete"); + + List> textAndTerms = theParams.remove(Constants.PARAM_TEXT); + addTextSearch(qb, bool, textAndTerms, "myNarrativeText"); + + if (bool.isEmpty()) { + return null; + } + + if (isNotBlank(theResourceName)) { + bool.must(qb.keyword().onField("myResourceType").matching(theResourceName).createQuery()); + } + + Query luceneQuery = bool.createQuery(); + + // wrap Lucene query in a javax.persistence.Query + FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, ResourceTable.class); + jpaQuery.setProjection("myId"); + + // execute search + List result = jpaQuery.getResultList(); + + ArrayList retVal = new ArrayList(); + for (Object object : result) { + Object[] nextArray = (Object[]) object; + retVal.add((Long)nextArray[0]); + } + + return retVal; + } + + private void addTextSearch(QueryBuilder qb, BooleanJunction bool, List> contentAndTerms, String field) { + if (contentAndTerms == null) { + return; + } + for (List nextAnd : contentAndTerms) { + Set terms = new HashSet(); + for (IQueryParameterType nextOr : nextAnd) { + StringParam nextOrString = (StringParam) nextOr; + String nextValueTrimmed = StringUtils.defaultString(nextOrString.getValue()).trim(); + if (isNotBlank(nextValueTrimmed)) { + terms.add(nextValueTrimmed); + } + } + if (terms.isEmpty() == false) { + String joinedTerms = StringUtils.join(terms, ' '); + bool.must(qb.keyword().onField(field).matching(joinedTerms).createQuery()); + } + } + } + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java index afff97a4728..268bb99c023 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.jpa.dao; +import java.util.Collection; + /* * #%L * HAPI FHIR JPA Server @@ -121,7 +123,7 @@ public interface IFhirResourceDao extends IDao { Set searchForIds(String theParameterName, IQueryParameterType theValue); - Set searchForIdsWithAndOr(SearchParameterMap theParams); + Set searchForIdsWithAndOr(SearchParameterMap theParams, Collection theInitialPids); DaoMethodOutcome update(T theResource); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchDao.java index a88683d4b8f..f273037aadb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ISearchDao.java @@ -1,11 +1,9 @@ package ca.uhn.fhir.jpa.dao; -import ca.uhn.fhir.rest.server.IBundleProvider; +import java.util.List; public interface ISearchDao { - public static final String FULL_TEXT_PARAM_NAME = "fullTextSearch"; - - IBundleProvider search(SearchParameterMap theParams); + List search(String theResourceName, SearchParameterMap theParams); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchDao.java deleted file mode 100644 index bca666da509..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchDao.java +++ /dev/null @@ -1,53 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; - -import org.hibernate.search.jpa.FullTextEntityManager; -import org.hibernate.search.jpa.FullTextQuery; -import org.hibernate.search.query.dsl.QueryBuilder; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.transaction.annotation.Transactional; - -import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; -import ca.uhn.fhir.rest.server.IBundleProvider; - -public class SearchDao extends BaseHapiFhirDao implements ISearchDao { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDao.class); - - @PersistenceContext(type = PersistenceContextType.TRANSACTION) - private EntityManager myEntityManager; - - @Transactional() - @Override - public IBundleProvider search(SearchParameterMap theParams) { - - FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); - - for (String nextParamName : theParams.keySet()) { - if (nextParamName.equals(FULL_TEXT_PARAM_NAME)) { - QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(ResourceIndexedSearchParamString.class).get(); - org.apache.lucene.search.Query luceneQuery = qb - .keyword() - .onFields("myValueComplete") - .matching("AAAS") - .createQuery(); - - // wrap Lucene query in a javax.persistence.Query - FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, ResourceIndexedSearchParamString.class); - - // execute search - List result = jpaQuery.getResultList(); - for (Object object : result) { - ourLog.info(""+ object); - } - } - } - - return null; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java index a8f5cb7a7bf..b1ae0695232 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamCoords.java @@ -32,7 +32,6 @@ import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; //@formatter:off -@Indexed @Entity @Table(name = "HFJ_SPIDX_COORDS" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_COORDS", indexes = { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java index 76d40772872..62b372fc60f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java @@ -36,7 +36,6 @@ import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; //@formatter:off -@Indexed @Entity @Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_DATE", indexes= { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java index acd946705d8..34d99264ce2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamNumber.java @@ -31,10 +31,13 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.annotations.Field; +import org.hibernate.search.annotations.FieldBridge; import org.hibernate.search.annotations.Indexed; +import org.hibernate.search.annotations.NumericField; + +import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge; //@formatter:off -@Indexed @Entity @Table(name = "HFJ_SPIDX_NUMBER" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ ) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_NUMBER", indexes= { @@ -47,6 +50,8 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP @Column(name = "SP_VALUE", nullable = true) @Field + @NumericField + @FieldBridge(impl = BigDecimalNumericFieldBridge.class) public BigDecimal myValue; public ResourceIndexedSearchParamNumber() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java index b6bedef0aa1..5fcbfeb5f6c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java @@ -31,10 +31,13 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.annotations.Field; +import org.hibernate.search.annotations.FieldBridge; import org.hibernate.search.annotations.Indexed; +import org.hibernate.search.annotations.NumericField; + +import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge; //@formatter:off -@Indexed @Entity @Table(name = "HFJ_SPIDX_QUANTITY" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ ) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_QUANTITY", indexes= { @@ -57,6 +60,8 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc @Column(name = "SP_VALUE", nullable = true) @Field + @NumericField + @FieldBridge(impl = BigDecimalNumericFieldBridge.class) public BigDecimal myValue; public ResourceIndexedSearchParamQuantity() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java index d13aa1df534..6cb4406e1ff 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java @@ -32,7 +32,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; -@Indexed @Entity @Table(name = "HFJ_SPIDX_STRING"/* , indexes= {@Index(name="IDX_SP_STRING", columnList="SP_VALUE_NORMALIZED")} */) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_STRING", indexes = { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java index 750728e4341..79164dc37db 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamToken.java @@ -32,7 +32,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; -@Indexed @Entity @Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java index ed28bd2ad49..51b0409c681 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java @@ -33,7 +33,6 @@ import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Indexed; //@formatter:off -@Indexed @Entity @Table(name = "HFJ_SPIDX_URI" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_URI", indexes = { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java index 2286bca9b18..6e41ca931b1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceTable.java @@ -35,14 +35,21 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Index; +import javax.persistence.Lob; import javax.persistence.OneToMany; import javax.persistence.Table; +import org.hibernate.search.annotations.Field; +import org.hibernate.search.annotations.Indexed; +import org.hibernate.search.annotations.IndexedEmbedded; + +import ca.uhn.fhir.jpa.search.IndexNonDeletedInterceptor; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; //@formatter:off +@Indexed(interceptor=IndexNonDeletedInterceptor.class) @Entity @Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes= { @Index(name = "IDX_RES_DATE", columnList="RES_UPDATED"), @@ -76,43 +83,58 @@ public class ResourceTable extends BaseHasResource implements Serializable { @Column(name = "RES_LANGUAGE", length = MAX_LANGUAGE_LENGTH, nullable = true) private String myLanguage; + /** + * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB + */ + @Column(name = "SP_NARRATIVE_TEXT") + @Lob + @Field() + private String myNarrativeText; + @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsCoords; @Column(name = "SP_COORDS_PRESENT") private boolean myParamsCoordsPopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsDate; @Column(name = "SP_DATE_PRESENT") private boolean myParamsDatePopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsNumber; @Column(name = "SP_NUMBER_PRESENT") private boolean myParamsNumberPopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsQuantity; @Column(name = "SP_QUANTITY_PRESENT") private boolean myParamsQuantityPopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsString; @Column(name = "SP_STRING_PRESENT") private boolean myParamsStringPopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsToken; @Column(name = "SP_TOKEN_PRESENT") private boolean myParamsTokenPopulated; @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) + @IndexedEmbedded private Collection myParamsUri; @Column(name = "SP_URI_PRESENT") @@ -125,6 +147,7 @@ public class ResourceTable extends BaseHasResource implements Serializable { private Collection myResourceLinks; @Column(name = "RES_TYPE", length = RESTYPE_LEN) + @Field private String myResourceType; @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @@ -297,6 +320,10 @@ public class ResourceTable extends BaseHasResource implements Serializable { myLanguage = theLanguage; } + public void setNarrativeTextParsedIntoWords(String theNarrativeText) { + myNarrativeText = theNarrativeText; + } + public void setParamsCoords(Collection theParamsCoords) { if (!isParamsTokenPopulated() && theParamsCoords.isEmpty()) { return; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java new file mode 100644 index 00000000000..e708e6bca0d --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.jpa.search; + +import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor; +import org.hibernate.search.indexes.interceptor.IndexingOverride; + +import ca.uhn.fhir.jpa.entity.ResourceTable; + +/** + * Only store non-deleted resources + */ +public class IndexNonDeletedInterceptor implements EntityIndexingInterceptor { + + @Override + public IndexingOverride onAdd(ResourceTable entity) { + if (entity.getDeleted() == null) { + return IndexingOverride.APPLY_DEFAULT; + } + return IndexingOverride.SKIP; + } + + @Override + public IndexingOverride onUpdate(ResourceTable entity) { + if (entity.getDeleted() == null) { + return IndexingOverride.UPDATE; + } + return IndexingOverride.REMOVE; + } + + @Override + public IndexingOverride onDelete(ResourceTable entity) { + return IndexingOverride.APPLY_DEFAULT; + } + + @Override + public IndexingOverride onCollectionUpdate(ResourceTable entity) { + return onUpdate(entity); + } +} \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/BigDecimalNumericFieldBridge.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/BigDecimalNumericFieldBridge.java new file mode 100644 index 00000000000..444383addd1 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/BigDecimalNumericFieldBridge.java @@ -0,0 +1,42 @@ +package ca.uhn.fhir.jpa.util; + +import java.math.BigDecimal; + +import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexableField; +import org.hibernate.search.bridge.LuceneOptions; +import org.hibernate.search.bridge.TwoWayFieldBridge; + +public class BigDecimalNumericFieldBridge implements TwoWayFieldBridge { + @Override + public void set(String name, Object value, Document document, LuceneOptions luceneOptions) { + if (value == null) { + if (luceneOptions.indexNullAs() != null) { + luceneOptions.addFieldToDocument(name, luceneOptions.indexNullAs(), document); + } + } else { + BigDecimal bdValue = (BigDecimal)value; + applyToLuceneOptions(luceneOptions, name, bdValue.doubleValue(), document); + } + } + + @Override + public final String objectToString(final Object object) { + return object == null ? null : object.toString(); + } + + @Override + public Object get(final String name, final Document document) { + final IndexableField field = document.getField(name); + if (field != null) { + Double doubleVal = (Double)field.numericValue(); + return new BigDecimal(doubleVal); + } else { + return null; + } + } + + protected void applyToLuceneOptions(LuceneOptions luceneOptions, String name, Number value, Document document) { + luceneOptions.addNumericFieldToDocument(name, value, document); + } +} \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-search-config-dstu2.xml b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-search-config-dstu2.xml index 43ee2ce9434..2e9740395a7 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-search-config-dstu2.xml +++ b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-search-config-dstu2.xml @@ -16,6 +16,6 @@ - + \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaDstu2Test.java index 273b44164e0..4c64baa7f29 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaDstu2Test.java @@ -168,7 +168,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest { } protected T loadResourceFromClasspath(Class type, String resourceName) throws IOException { - InputStream stream = FhirResourceDaoDstu2SearchTest.class.getResourceAsStream(resourceName); + InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName); if (stream == null) { fail("Unable to load resource: " + resourceName); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchFtTest.java similarity index 99% rename from hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchTest.java rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchFtTest.java index b2a65417e20..083835f0439 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchFtTest.java @@ -94,8 +94,8 @@ import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @SuppressWarnings("unchecked") -public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchTest.class); +public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchFtTest.class); @Test public void testSearchWithEmptySort() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchNoFtTest.java new file mode 100644 index 00000000000..5f327bfcbc2 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2SearchNoFtTest.java @@ -0,0 +1,53 @@ +package ca.uhn.fhir.jpa.dao; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Test; + +import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.dstu2.resource.Observation; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.rest.server.Constants; + +@SuppressWarnings("unchecked") +public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchNoFtTest.class); + + @Test + public void testSearchWithChainedParams() { + String methodName = "testSearchWithChainedParams"; + IIdType pId1; + { + Patient patient = new Patient(); + patient.addName().addGiven("methodName"); + patient.addAddress().addLine("My fulltext address"); + pId1 = myPatientDao.create(patient).getId(); + } + + Observation obs = new Observation(); + obs.getSubject().setReference(pId1); + obs.setValue(new StringDt("This is the fulltext of the observation")); + IIdType oId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + + obs = new Observation(); + obs.getSubject().setReference(pId1); + obs.setValue(new StringDt("Another fulltext")); + IIdType oId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Constants.PARAM_CONTENT, new StringDt("fulltext")); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); + assertThat(patients, containsInAnyOrder(oId1, oId2)); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSearchDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSearchDaoDstu2Test.java index 3cf2db6394d..55d9975b61d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSearchDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSearchDaoDstu2Test.java @@ -1,13 +1,26 @@ package ca.uhn.fhir.jpa.dao; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.Search; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.annotation.Transactional; +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.server.Constants; @ContextConfiguration(locations = { "classpath:fhir-spring-search-config-dstu2.xml" }) public class FhirSearchDaoDstu2Test extends BaseJpaDstu2Test { @@ -15,25 +28,159 @@ public class FhirSearchDaoDstu2Test extends BaseJpaDstu2Test { @Autowired private ISearchDao mySearchDao; + @Before + @Transactional + public void beforeFlushFT() { + FullTextEntityManager ftem = Search.getFullTextEntityManager(myEntityManager); + ftem.purgeAll(ResourceTable.class); + ftem.flushToIndexes(); + } + @Test - public void testStringSearch() { + public void testContentSearch() { + Long id1; { Patient patient = new Patient(); patient.addIdentifier().setSystem("urn:system").setValue("001"); patient.addName().addGiven("testSearchStringParamWithNonNormalized_h\u00F6ra"); patient.addName().addFamily("AAAS"); - myPatientDao.create(patient); + patient.addName().addFamily("CCC"); + id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getIdPartAsLong(); } + Long id2; { Patient patient = new Patient(); patient.addIdentifier().setSystem("urn:system").setValue("002"); patient.addName().addGiven("testSearchStringParamWithNonNormalized_HORA"); - myPatientDao.create(patient); + patient.addName().addFamily("AAAB"); + patient.addName().addFamily("CCC"); + id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getIdPartAsLong(); + } + Long id3; + { + Organization org = new Organization(); + org.setName("DDD"); + id3 = myOrganizationDao.create(org).getId().toUnqualifiedVersionless().getIdPartAsLong(); } SearchParameterMap map = new SearchParameterMap(); - map.add(ISearchDao.FULL_TEXT_PARAM_NAME, new StringAndListParam().addAnd(new StringOrListParam().addOr(new StringParam("AAA")))); - mySearchDao.search(map); + String resourceName = "Patient"; + + // One term + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS"))); + + map.add(Constants.PARAM_CONTENT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1)); + } + // OR + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")).addOr(new StringParam("AAAB"))); + + map.add(Constants.PARAM_CONTENT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1, id2)); + } + // AND + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS"))); + content.addAnd(new StringOrListParam().addOr(new StringParam("CCC"))); + + map.add(Constants.PARAM_CONTENT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1)); + } + // AND OR + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAB")).addOr(new StringParam("AAAS"))); + content.addAnd(new StringOrListParam().addOr(new StringParam("CCC"))); + + map.add(Constants.PARAM_CONTENT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1, id2)); + } + // All Resource Types + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("CCC")).addOr(new StringParam("DDD"))); + + map.add(Constants.PARAM_CONTENT, content); + List found = mySearchDao.search(null, map); + assertThat(found, containsInAnyOrder(id1, id2, id3)); + } + } + @Test + public void testNarrativeSearch() { + Long id1; + { + Patient patient = new Patient(); + patient.getText().setDiv("
AAAS

FOO

CCC
"); + id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getIdPartAsLong(); + } + Long id2; + { + Patient patient = new Patient(); + patient.getText().setDiv("
AAAB

FOO

CCC
"); + id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getIdPartAsLong(); + } + + SearchParameterMap map = new SearchParameterMap(); + String resourceName = "Patient"; + + // One term + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS"))); + + map.add(Constants.PARAM_TEXT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1)); + } + // OR + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS")).addOr(new StringParam("AAAB"))); + + map.add(Constants.PARAM_TEXT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1, id2)); + } + // AND + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAS"))); + content.addAnd(new StringOrListParam().addOr(new StringParam("CCC"))); + + map.add(Constants.PARAM_TEXT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1)); + } + // AND OR + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("AAAB")).addOr(new StringParam("AAAS"))); + content.addAnd(new StringOrListParam().addOr(new StringParam("CCC"))); + + map.add(Constants.PARAM_TEXT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, containsInAnyOrder(id1, id2)); + } + // Tag Contents + { + StringAndListParam content = new StringAndListParam(); + content.addAnd(new StringOrListParam().addOr(new StringParam("div"))); + + map.add(Constants.PARAM_TEXT, content); + List found = mySearchDao.search(resourceName, map); + assertThat(found, empty()); + } + } + } diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 968cbab880a..7836f855a3d 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -50,6 +50,14 @@ public class ${className}ResourceProvider extends @OptionalParam(name="_language") StringAndListParam theResourceLanguage, + @Description(shortDefinition="Search the contents of the resource's data using a fulltext search") + @OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_CONTENT) + StringAndListParam theFtContent, + + @Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search") + @OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_TEXT) + StringAndListParam theFtText, + @Description(shortDefinition="Search for resources which have the given tag") @OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_TAG) TokenAndListParam theSearchForTag, @@ -128,6 +136,8 @@ public class ${className}ResourceProvider extends SearchParameterMap paramMap = new SearchParameterMap(); paramMap.add("_id", theId); paramMap.add("_language", theResourceLanguage); + paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_CONTENT, theFtContent); + paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_TEXT, theFtText); paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_TAG, theSearchForTag); paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_SECURITY, theSearchForSecurity); paramMap.add(ca.uhn.fhir.rest.server.Constants.PARAM_PROFILE, theSearchForProfile);