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