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.item.server.indexer;
018
019import com.echothree.model.control.core.common.MimeTypeUsageTypes;
020import com.echothree.model.control.index.common.IndexConstants;
021import com.echothree.model.control.index.common.IndexFieldVariations;
022import com.echothree.model.control.index.common.IndexFields;
023import com.echothree.model.control.index.server.indexer.BaseIndexer;
024import com.echothree.model.control.index.server.indexer.FieldTypes;
025import com.echothree.model.control.index.server.indexer.IndexerDebugFlags;
026import com.echothree.model.control.index.server.indexer.sortabledescriptionproducer.SortableDescriptionProducer;
027import com.echothree.model.control.index.server.indexer.sortabledescriptionproducer.SortableDescriptionProducerFactory;
028import com.echothree.model.control.item.server.analyzer.ItemAnalyzer;
029import com.echothree.model.control.item.server.control.ItemControl;
030import com.echothree.model.data.core.server.entity.EntityInstance;
031import com.echothree.model.data.index.server.entity.Index;
032import com.echothree.model.data.item.server.entity.Item;
033import com.echothree.model.data.item.server.entity.ItemDescriptionType;
034import com.echothree.util.server.message.ExecutionErrorAccumulator;
035import java.util.List;
036import javax.enterprise.context.Dependent;
037import javax.inject.Inject;
038import org.apache.lucene.analysis.Analyzer;
039import org.apache.lucene.document.Document;
040import org.apache.lucene.document.Field;
041import org.apache.lucene.document.LongPoint;
042import org.apache.lucene.document.SortedDocValuesField;
043import org.apache.lucene.util.BytesRef;
044
045@Dependent
046public class ItemIndexer
047        extends BaseIndexer<Item> {
048
049    @Inject
050    ItemControl itemControl;
051    
052    List<ItemDescriptionType> itemDescriptionTypes;
053    SortableDescriptionProducer sortableDescriptionProducer;
054
055    @Override
056    public BaseIndexer<Item> setup(final ExecutionErrorAccumulator eea, final Index index) {
057        itemDescriptionTypes = itemControl.getItemDescriptionTypesByIncludeInIndex();
058        sortableDescriptionProducer = SortableDescriptionProducerFactory.getInstance().getSortableDescriptionProducer(index.getLastDetail().getLanguage());
059
060        return super.setup(eea, index);
061    }
062
063    @Override
064    protected Analyzer getAnalyzer() {
065        return new ItemAnalyzer(eea, language, entityType, entityAliasTypes, entityAttributes, tagScopes);
066    }
067    
068    @Override
069    protected Item getEntity(final EntityInstance entityInstance) {
070        return itemControl.getItemByEntityInstance(entityInstance);
071    }
072
073    @Override
074    protected Document convertToDocument(final EntityInstance entityInstance, final Item item) {
075        var itemDetail = item.getLastDetail();
076        var itemAliases = itemControl.getItemAliasesByItem(item);
077        var itemDeliveryType = itemDetail.getItemDeliveryType();
078        var itemInventoryType = itemDetail.getItemInventoryType();
079        var itemAccountingCategory = itemDetail.getItemAccountingCategory();
080        var itemPurchasingCategory = itemDetail.getItemPurchasingCategory();
081        var inventorySerialized = itemDetail.getInventorySerialized();
082        var shippingEndTime = itemDetail.getShippingEndTime();
083        var salesOrderEndTime = itemDetail.getSalesOrderEndTime();
084        var purchaseOrderStartTime = itemDetail.getPurchaseOrderStartTime();
085        var purchaseOrderEndTime = itemDetail.getPurchaseOrderEndTime();
086
087        var document = newDocumentWithEntityInstanceFields(entityInstance, item.getPrimaryKey());
088
089        document.add(new Field(IndexFields.itemName.name(), itemDetail.getItemName(), FieldTypes.NOT_STORED_TOKENIZED));
090        document.add(new SortedDocValuesField(IndexFields.itemName.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(),
091                new BytesRef(itemDetail.getItemName())));
092        document.add(new Field(IndexFields.itemNameAndAliases.name(), itemDetail.getItemName(), FieldTypes.NOT_STORED_TOKENIZED));
093
094        document.add(new Field(IndexFields.itemTypeName.name(), itemDetail.getItemType().getItemTypeName(), FieldTypes.NOT_STORED_TOKENIZED));
095        document.add(new Field(IndexFields.itemUseTypeName.name(), itemDetail.getItemUseType().getItemUseTypeName(), FieldTypes.NOT_STORED_TOKENIZED));
096
097        if(itemDeliveryType != null) {
098            document.add(new Field(IndexFields.itemDeliveryTypeName.name(), itemDeliveryType.getItemDeliveryTypeName(), FieldTypes.NOT_STORED_TOKENIZED));
099        }
100        
101        if(itemInventoryType != null) {
102            document.add(new Field(IndexFields.itemInventoryTypeName.name(), itemInventoryType.getItemInventoryTypeName(), FieldTypes.NOT_STORED_TOKENIZED));
103        }
104        
105        itemAliases.stream().map((itemAlias) -> {
106            document.add(new Field(IndexFields.aliases.name(), itemAlias.getAlias(), FieldTypes.NOT_STORED_TOKENIZED));
107            return itemAlias;
108        }).map((itemAlias) -> {
109            document.add(new Field(IndexFields.itemNameAndAliases.name(), itemAlias.getAlias(), FieldTypes.NOT_STORED_TOKENIZED));
110            return itemAlias;
111        }).forEach((itemAlias) -> {
112            document.add(new Field(itemAlias.getItemAliasType().getLastDetail().getItemAliasTypeName(), itemAlias.getAlias(), FieldTypes.NOT_STORED_TOKENIZED));
113        });
114        
115        document.add(new Field(IndexFields.itemCategoryName.name(), itemDetail.getItemCategory().getLastDetail().getItemCategoryName(), FieldTypes.NOT_STORED_TOKENIZED));
116        
117        if(itemAccountingCategory != null) {
118            document.add(new Field(IndexFields.itemAccountingCategoryName.name(), itemDetail.getItemAccountingCategory().getLastDetail().getItemAccountingCategoryName(), FieldTypes.NOT_STORED_TOKENIZED));
119        }
120        
121        if(itemPurchasingCategory != null) {
122            document.add(new Field(IndexFields.itemPurchasingCategoryName.name(), itemDetail.getItemPurchasingCategory().getLastDetail().getItemPurchasingCategoryName(), FieldTypes.NOT_STORED_TOKENIZED));
123        }
124        
125        if(inventorySerialized != null) {
126            document.add(new Field(IndexFields.inventorySerialized.name(), itemDetail.getInventorySerialized().toString(), FieldTypes.NOT_STORED_TOKENIZED));
127        }
128        
129        document.add(new Field(IndexFields.shippingChargeExempt.name(), itemDetail.getShippingChargeExempt().toString(), FieldTypes.NOT_STORED_TOKENIZED));
130        document.add(new LongPoint(IndexFields.shippingStartTime.name(), itemDetail.getShippingStartTime()));
131        if(shippingEndTime != null) {
132            document.add(new LongPoint(IndexFields.shippingEndTime.name(), shippingEndTime));
133        }
134        document.add(new LongPoint(IndexFields.salesOrderStartTime.name(), itemDetail.getSalesOrderStartTime()));
135        if(salesOrderEndTime != null) {
136            document.add(new LongPoint(IndexFields.salesOrderEndTime.name(), salesOrderEndTime));
137        }
138        if(purchaseOrderStartTime != null) {
139            document.add(new LongPoint(IndexFields.purchaseOrderStartTime.name(), purchaseOrderStartTime));
140        }
141        if(purchaseOrderEndTime != null) {
142            document.add(new LongPoint(IndexFields.purchaseOrderEndTime.name(), purchaseOrderEndTime));
143        }
144        document.add(new Field(IndexFields.allowClubDiscounts.name(), itemDetail.getAllowClubDiscounts().toString(), FieldTypes.NOT_STORED_TOKENIZED));
145        document.add(new Field(IndexFields.allowCouponDiscounts.name(), itemDetail.getAllowCouponDiscounts().toString(), FieldTypes.NOT_STORED_TOKENIZED));
146        document.add(new Field(IndexFields.allowAssociatePayments.name(), itemDetail.getAllowAssociatePayments().toString(), FieldTypes.NOT_STORED_TOKENIZED));
147
148        document.add(new Field(IndexFields.unitOfMeasureKindName.name(), itemDetail.getUnitOfMeasureKind().getLastDetail().getUnitOfMeasureKindName(), FieldTypes.NOT_STORED_TOKENIZED));
149        document.add(new Field(IndexFields.itemPriceTypeName.name(), itemDetail.getItemPriceType().getItemPriceTypeName(), FieldTypes.NOT_STORED_TOKENIZED));
150
151        itemDescriptionTypes.forEach((itemDescriptionType) -> {
152            var itemDescription = itemControl.getBestItemDescription(itemDescriptionType, item, language);
153            if (itemDescription != null) {
154                var itemDescriptionTypeDetail = itemDescriptionType.getLastDetail();
155                var mimeTypeUsageType = itemDescriptionTypeDetail.getMimeTypeUsageType();
156                var itemDescriptionTypeName = itemDescriptionTypeDetail.getItemDescriptionTypeName();
157
158                if(mimeTypeUsageType == null) {
159                    var itemStringDescription = itemControl.getItemStringDescription(itemDescription);
160                    var stringDescription = itemStringDescription.getStringDescription();
161                    
162                    document.add(new Field(itemDescriptionTypeName, stringDescription, FieldTypes.NOT_STORED_TOKENIZED));
163                    document.add(new Field(itemDescriptionTypeName + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.dictionary.name(),
164                            stringDescription, FieldTypes.NOT_STORED_TOKENIZED));
165                    document.add(new SortedDocValuesField(itemDescriptionTypeName + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(),
166                            new BytesRef(sortableDescriptionProducer.getSortableDescription(stringDescription))));
167
168                    if(IndexerDebugFlags.LogItemIndexing) {
169                        log.info("--- " + itemDescriptionTypeName + ", stringDescription = " + stringDescription);
170                    }
171                } else {
172                    var mimeTypeUsageTypeName = mimeTypeUsageType.getMimeTypeUsageTypeName();
173
174                    if(mimeTypeUsageTypeName.equals(MimeTypeUsageTypes.TEXT.name())) {
175                        var itemClobDescription = itemControl.getItemClobDescription(itemDescription);
176                        var clobDescription = itemClobDescription.getClobDescription();
177
178                        // TODO: mime type conversion to text/plain happens here
179                        document.add(new Field(itemDescriptionTypeName, clobDescription, FieldTypes.NOT_STORED_TOKENIZED));
180
181                        if(IndexerDebugFlags.LogItemIndexing) {
182                            log.info("--- " + itemDescriptionTypeName + ", clobDescription = " + clobDescription);
183                        }
184                    } // Others are not supported at this time, DOCUMENT probably should be.
185                }
186            }
187        });
188
189        return document;
190    }
191
192}