001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.entity;
021
022import org.hibernate.search.engine.backend.document.DocumentElement;
023import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement;
024import org.hibernate.search.engine.backend.types.dsl.IndexFieldTypeFactory;
025import org.hibernate.search.mapper.pojo.bridge.PropertyBridge;
026import org.hibernate.search.mapper.pojo.bridge.binding.PropertyBindingContext;
027import org.hibernate.search.mapper.pojo.bridge.mapping.programmatic.PropertyBinder;
028import org.hibernate.search.mapper.pojo.bridge.runtime.PropertyBridgeWriteContext;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import java.util.Collection;
033
034import static org.apache.commons.lang3.StringUtils.isNotBlank;
035
036/**
037 * Allows hibernate search to index individual concepts' properties
038 */
039public class TermConceptPropertyBinder implements PropertyBinder {
040
041        public static final String CONCEPT_PROPERTY_PREFIX_NAME = "P:";
042
043        private static final Logger ourLog = LoggerFactory.getLogger(TermConceptPropertyBinder.class);
044
045        @Override
046        public void bind(PropertyBindingContext thePropertyBindingContext) {
047                thePropertyBindingContext.dependencies().use("myKey").use("myValue");
048                IndexSchemaElement indexSchemaElement = thePropertyBindingContext.indexSchemaElement();
049
050                // In order to support dynamic fields, we have to use field templates. We _must_ define the template at
051                // bootstrap time and cannot
052                // create them adhoc.
053                // https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/#mapper-orm-bridge-index-field-dsl-dynamic
054                indexSchemaElement
055                                .fieldTemplate("propTemplate", IndexFieldTypeFactory::asString)
056                                .matchingPathGlob(CONCEPT_PROPERTY_PREFIX_NAME + "*")
057                                .multiValued();
058
059                thePropertyBindingContext.bridge(Collection.class, new TermConceptPropertyBridge());
060        }
061
062        @SuppressWarnings("rawtypes")
063        private static class TermConceptPropertyBridge implements PropertyBridge<Collection> {
064
065                @Override
066                public void write(
067                                DocumentElement theDocument,
068                                Collection theObject,
069                                PropertyBridgeWriteContext thePropertyBridgeWriteContext) {
070
071                        @SuppressWarnings("unchecked")
072                        Collection<TermConceptProperty> properties = (Collection<TermConceptProperty>) theObject;
073
074                        if (properties != null) {
075                                for (TermConceptProperty next : properties) {
076                                        theDocument.addValue(CONCEPT_PROPERTY_PREFIX_NAME + next.getKey(), next.getValue());
077                                        ourLog.trace(
078                                                        "Adding Prop: {}{} -- {}", CONCEPT_PROPERTY_PREFIX_NAME, next.getKey(), next.getValue());
079                                        if (next.getType() == TermConceptPropertyTypeEnum.CODING && isNotBlank(next.getDisplay())) {
080                                                theDocument.addValue(CONCEPT_PROPERTY_PREFIX_NAME + next.getKey(), next.getDisplay());
081                                                ourLog.trace(
082                                                                "Adding multivalue Prop: {}{} -- {}",
083                                                                CONCEPT_PROPERTY_PREFIX_NAME,
084                                                                next.getKey(),
085                                                                next.getDisplay());
086                                        }
087                                }
088                        }
089                }
090        }
091}