001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.model.control.contact.server.indexer; 018 019import com.echothree.model.control.contact.common.ContactMechanismTypes; 020import com.echothree.model.control.contact.server.analyzer.ContactMechanismAnalyzer; 021import com.echothree.model.control.contact.server.control.ContactControl; 022import com.echothree.model.control.geo.server.control.GeoControl; 023import com.echothree.model.control.index.common.IndexConstants; 024import com.echothree.model.control.index.common.IndexFieldVariations; 025import com.echothree.model.control.index.common.IndexFields; 026import com.echothree.model.control.index.server.indexer.BaseIndexer; 027import com.echothree.model.control.index.server.indexer.FieldTypes; 028import com.echothree.model.data.contact.server.entity.ContactMechanism; 029import com.echothree.model.data.contact.server.entity.ContactMechanismPurpose; 030import com.echothree.model.data.core.server.entity.EntityInstance; 031import com.echothree.model.data.geo.server.entity.GeoCode; 032import com.echothree.model.data.index.server.entity.Index; 033import com.echothree.model.data.party.server.entity.Language; 034import com.echothree.model.data.party.server.entity.Party; 035import com.echothree.model.data.party.server.entity.PartyType; 036import com.echothree.util.common.string.Inet4AddressUtils; 037import com.echothree.util.server.message.ExecutionErrorAccumulator; 038import java.util.HashSet; 039import java.util.Set; 040import javax.enterprise.context.Dependent; 041import javax.inject.Inject; 042import org.apache.lucene.analysis.Analyzer; 043import org.apache.lucene.document.Document; 044import org.apache.lucene.document.Field; 045import org.apache.lucene.document.SortedDocValuesField; 046import org.apache.lucene.util.BytesRef; 047 048@Dependent 049public class ContactMechanismIndexer 050 extends BaseIndexer<ContactMechanism> { 051 052 @Inject 053 ContactControl contactControl; 054 055 @Inject 056 GeoControl geoControl; 057 058 @Override 059 public BaseIndexer<ContactMechanism> setup(final ExecutionErrorAccumulator eea, final Index index) { 060 return super.setup(eea, index); 061 } 062 063 @Override 064 protected Analyzer getAnalyzer() { 065 return new ContactMechanismAnalyzer(eea, language, entityType, entityAliasTypes, entityAttributes, tagScopes); 066 } 067 068 @Override 069 protected ContactMechanism getEntity(final EntityInstance entityInstance) { 070 return contactControl.getContactMechanismByEntityInstance(entityInstance); 071 } 072 073 private void addPartyNamesToDocument(Document document, Set<Party> parties) { 074 var partyNamesBuilder = new StringBuilder(); 075 076 parties.forEach((party) -> { 077 if(partyNamesBuilder.length() != 0) { 078 partyNamesBuilder.append(' '); 079 } 080 081 partyNamesBuilder.append(party.getLastDetail().getPartyName()); 082 }); 083 084 document.add(new Field(IndexFields.partyNames.name(), partyNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED)); 085 } 086 087 private void addPartyTypeNamesToDocument(Document document, Set<PartyType> partyTypes) { 088 var partyTypeNamesBuilder = new StringBuilder(); 089 090 partyTypes.forEach((partyType) -> { 091 if(partyTypeNamesBuilder.length() != 0) { 092 partyTypeNamesBuilder.append(' '); 093 } 094 095 partyTypeNamesBuilder.append(partyType.getPartyTypeName()); 096 }); 097 098 document.add(new Field(IndexFields.partyTypeNames.name(), partyTypeNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED)); 099 } 100 101 private void addContactMechanismPurposeNamesToDocument(Document document, Set<ContactMechanismPurpose> contactMechanismPurposes) { 102 var contactMechanismPurposesNamesBuilder = new StringBuilder(); 103 104 contactMechanismPurposes.forEach((contactMechanismPurpose) -> { 105 if(contactMechanismPurposesNamesBuilder.length() != 0) { 106 contactMechanismPurposesNamesBuilder.append(' '); 107 } 108 109 contactMechanismPurposesNamesBuilder.append(contactMechanismPurpose.getContactMechanismPurposeName()); 110 }); 111 112 document.add(new Field(IndexFields.contactMechanismPurposeNames.name(), contactMechanismPurposesNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED)); 113 } 114 115 private void addPartiesToDocument(final Document document, final ContactMechanism contactMechanism) { 116 Set<Party> parties = new HashSet<>(); 117 Set<PartyType> partyTypes = new HashSet<>(); 118 Set<ContactMechanismPurpose> contactMechanismPurposes = new HashSet<>(); 119 120 contactControl.getPartyContactMechanismsByContactMechanism(contactMechanism).forEach((partyContactMechanism) -> { 121 var partyContactMechanismDetail = partyContactMechanism.getLastDetail(); 122 var party = partyContactMechanismDetail.getParty(); 123 parties.add(party); 124 partyTypes.add(party.getLastDetail().getPartyType()); 125 contactControl.getPartyContactMechanismPurposesByPartyContactMechanism(partyContactMechanism).forEach((partyContactMechanismPurpose) -> { 126 contactMechanismPurposes.add(partyContactMechanismPurpose.getLastDetail().getContactMechanismPurpose()); 127 }); 128 }); 129 130 addPartyNamesToDocument(document, parties); 131 addPartyTypeNamesToDocument(document, partyTypes); 132 addContactMechanismPurposeNamesToDocument(document, contactMechanismPurposes); 133 } 134 135 private void addGeoCodeToDocument(final Document document, final Language language, final GeoCode geoCode, String description, final String geoCodeIndexField, final String descriptionIndexField) { 136 if(geoCode != null) { 137 description = geoControl.getBestGeoCodeDescription(geoCode, language); 138 139 document.add(new Field(geoCodeIndexField, geoCode.getLastDetail().getGeoCodeName(), FieldTypes.NOT_STORED_TOKENIZED)); 140 } 141 142 if(description != null) { 143 document.add(new Field(descriptionIndexField, description, FieldTypes.NOT_STORED_TOKENIZED)); 144 } 145 } 146 147 private void addContactEmailAddressToDocument(final Document document, final ContactMechanism contactMechanism) { 148 var contactEmailAddress = contactControl.getContactEmailAddress(contactMechanism); 149 150 if(contactEmailAddress != null) { 151 document.add(new Field(IndexFields.emailAddress.name(), contactEmailAddress.getEmailAddress(), FieldTypes.NOT_STORED_TOKENIZED)); 152 } 153 } 154 155 private void addContactInet4AddressToDocument(final Document document, final ContactMechanism contactMechanism) { 156 var contactInet4Address = contactControl.getContactInet4Address(contactMechanism); 157 158 if(contactInet4Address != null) { 159 document.add(new Field(IndexFields.inet4Address.name(), Inet4AddressUtils.getInstance().formatInet4Address(contactInet4Address.getInet4Address()), FieldTypes.NOT_STORED_TOKENIZED)); 160 } 161 } 162 163 private void addContactPostalAddressToDocument(final Document document, final ContactMechanism contactMechanism, final Language language) { 164 var contactPostalAddress = contactControl.getContactPostalAddress(contactMechanism); 165 166 if(contactPostalAddress != null) { 167 var personalTitle = contactPostalAddress.getPersonalTitle(); 168 var firstName = contactPostalAddress.getFirstName(); 169 var middleName = contactPostalAddress.getMiddleName(); 170 var lastName = contactPostalAddress.getLastName(); 171 var nameSuffix = contactPostalAddress.getNameSuffix(); 172 var companyName = contactPostalAddress.getCompanyName(); 173 var attention = contactPostalAddress.getAttention(); 174 var address2 = contactPostalAddress.getAddress2(); 175 var address3 = contactPostalAddress.getAddress3(); 176 177 if(personalTitle != null) { 178 document.add(new Field(IndexFields.personalTitle.name(), personalTitle.getLastDetail().getDescription(), FieldTypes.NOT_STORED_TOKENIZED)); 179 } 180 181 if(firstName != null) { 182 document.add(new Field(IndexFields.firstName.name(), firstName, FieldTypes.NOT_STORED_TOKENIZED)); 183 } 184 185 if(middleName != null) { 186 document.add(new Field(IndexFields.middleName.name(), middleName, FieldTypes.NOT_STORED_TOKENIZED)); 187 } 188 189 if(lastName != null) { 190 document.add(new Field(IndexFields.lastName.name(), lastName, FieldTypes.NOT_STORED_TOKENIZED)); 191 } 192 193 if(nameSuffix != null) { 194 document.add(new Field(IndexFields.nameSuffix.name(), nameSuffix.getLastDetail().getDescription(), FieldTypes.NOT_STORED_TOKENIZED)); 195 } 196 197 if(companyName != null) { 198 document.add(new Field(IndexFields.companyName.name(), companyName, FieldTypes.NOT_STORED_TOKENIZED)); 199 } 200 201 if(attention != null) { 202 document.add(new Field(IndexFields.attention.name(), attention, FieldTypes.NOT_STORED_TOKENIZED)); 203 } 204 205 document.add(new Field(IndexFields.address1.name(), contactPostalAddress.getAddress1(), FieldTypes.NOT_STORED_TOKENIZED)); 206 207 if(address2 != null) { 208 document.add(new Field(IndexFields.address2.name(), address2, FieldTypes.NOT_STORED_TOKENIZED)); 209 } 210 211 if(address3 != null) { 212 document.add(new Field(IndexFields.address3.name(), address3, FieldTypes.NOT_STORED_TOKENIZED)); 213 } 214 215 addGeoCodeToDocument(document, language, contactPostalAddress.getCityGeoCode(), contactPostalAddress.getCity(), IndexFields.cityGeoCodeName.name(), IndexFields.city.name()); 216 addGeoCodeToDocument(document, language, contactPostalAddress.getCountyGeoCode(), null, IndexFields.countyGeoCodeName.name(), IndexFields.county.name()); 217 addGeoCodeToDocument(document, language, contactPostalAddress.getStateGeoCode(), contactPostalAddress.getState(), IndexFields.stateGeoCodeName.name(), IndexFields.state.name()); 218 addGeoCodeToDocument(document, language, contactPostalAddress.getPostalCodeGeoCode(), contactPostalAddress.getPostalCode(), IndexFields.postalCodeGeoCodeName.name(), IndexFields.postalCode.name()); 219 addGeoCodeToDocument(document, language, contactPostalAddress.getCountryGeoCode(), null, IndexFields.countryGeoCodeName.name(), IndexFields.country.name()); 220 221 document.add(new Field(IndexFields.isCommercial.name(), contactPostalAddress.getIsCommercial().toString(), FieldTypes.NOT_STORED_TOKENIZED)); 222 } 223 } 224 225 private void addContactTelephoneToDocument(final Document document, final ContactMechanism contactMechanism) { 226 var contactTelephone = contactControl.getContactTelephone(contactMechanism); 227 228 if(contactTelephone != null) { 229 var areaCode = contactTelephone.getAreaCode(); 230 var telephoneExtension = contactTelephone.getTelephoneExtension(); 231 232 document.add(new Field(IndexFields.countryGeoCodeName.name(), contactTelephone.getCountryGeoCode().getLastDetail().getGeoCodeName(), FieldTypes.NOT_STORED_TOKENIZED)); 233 234 if(areaCode != null) { 235 document.add(new Field(IndexFields.areaCode.name(), areaCode, FieldTypes.NOT_STORED_TOKENIZED)); 236 } 237 238 document.add(new Field(IndexFields.telephoneNumber.name(), contactTelephone.getTelephoneNumber(), FieldTypes.NOT_STORED_TOKENIZED)); 239 240 if(telephoneExtension != null) { 241 document.add(new Field(IndexFields.telephoneExtension.name(), telephoneExtension, FieldTypes.NOT_STORED_TOKENIZED)); 242 } 243 } 244 } 245 246 private void addContactWebAddressToDocument(final Document document, final ContactMechanism contactMechanism) { 247 var contactWebAddress = contactControl.getContactWebAddress(contactMechanism); 248 249 if(contactWebAddress != null) { 250 document.add(new Field(IndexFields.url.name(), contactWebAddress.getUrl(), FieldTypes.NOT_STORED_TOKENIZED)); 251 } 252 } 253 254 private void addContactMechanismToDocument(final Document document, final ContactMechanism contactMechanism, final Language language) { 255 var contactMechanismDetail = contactMechanism.getLastDetail(); 256 var contactMechanismTypeName = contactMechanismDetail.getContactMechanismType().getContactMechanismTypeName(); 257 258 document.add(new Field(IndexFields.contactMechanismName.name(), contactMechanismDetail.getContactMechanismName(), FieldTypes.NOT_STORED_TOKENIZED)); 259 document.add(new SortedDocValuesField(IndexFields.contactMechanismName.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(), 260 new BytesRef(contactMechanismDetail.getContactMechanismName()))); 261 262 document.add(new Field(IndexFields.contactMechanismTypeName.name(), contactMechanismTypeName, FieldTypes.NOT_STORED_TOKENIZED)); 263 document.add(new Field(IndexFields.allowSolicitation.name(), contactMechanismDetail.getAllowSolicitation().toString(), FieldTypes.NOT_STORED_TOKENIZED)); 264 265 if(contactMechanismTypeName.equals(ContactMechanismTypes.EMAIL_ADDRESS.name())) { 266 addContactEmailAddressToDocument(document, contactMechanism); 267 } else if(contactMechanismTypeName.equals(ContactMechanismTypes.INET_4.name())) { 268 addContactInet4AddressToDocument(document, contactMechanism); 269 } else if(contactMechanismTypeName.equals(ContactMechanismTypes.INET_6.name())) { 270 // TODO 271 } else if(contactMechanismTypeName.equals(ContactMechanismTypes.POSTAL_ADDRESS.name())) { 272 addContactPostalAddressToDocument(document, contactMechanism, language); 273 } else if(contactMechanismTypeName.equals(ContactMechanismTypes.TELECOM_ADDRESS.name())) { 274 addContactTelephoneToDocument(document, contactMechanism); 275 } else if(contactMechanismTypeName.equals(ContactMechanismTypes.WEB_ADDRESS.name())) { 276 addContactWebAddressToDocument(document, contactMechanism); 277 } 278 } 279 280 @Override 281 protected Document convertToDocument(final EntityInstance entityInstance, final ContactMechanism contactMechanism) { 282 var document = newDocumentWithEntityInstanceFields(entityInstance, contactMechanism.getPrimaryKey()); 283 284 addPartiesToDocument(document, contactMechanism); 285 addContactMechanismToDocument(document, contactMechanism, language); 286 287 return document; 288 } 289 290}