View Javadoc
1   package org.kuali.ole.docstore.engine.service.index.solr;
2   
3   import org.apache.commons.collections.CollectionUtils;
4   import org.apache.commons.lang.StringUtils;
5   import org.apache.solr.client.solrj.SolrQuery;
6   import org.apache.solr.client.solrj.SolrServer;
7   import org.apache.solr.client.solrj.SolrServerException;
8   import org.apache.solr.client.solrj.response.QueryResponse;
9   import org.apache.solr.client.solrj.response.UpdateResponse;
10  import org.apache.solr.common.SolrDocument;
11  import org.apache.solr.common.SolrInputDocument;
12  import org.kuali.ole.docstore.OleException;
13  import org.kuali.ole.docstore.common.constants.DocstoreConstants;
14  import org.kuali.ole.docstore.common.document.*;
15  import org.kuali.ole.docstore.common.document.config.DocumentSearchConfig;
16  import org.kuali.ole.docstore.common.document.content.bib.marc.*;
17  import org.kuali.ole.docstore.common.document.content.bib.marc.xstream.BibMarcRecordProcessor;
18  import org.kuali.ole.docstore.common.exception.DocstoreIndexException;
19  import org.kuali.ole.docstore.common.util.ReindexBatchStatistics;
20  import org.kuali.ole.docstore.discovery.service.SolrServerManager;
21  import org.kuali.ole.docstore.indexer.solr.DocumentLocalId;
22  import org.kuali.ole.docstore.model.enums.DocCategory;
23  import org.kuali.ole.docstore.model.enums.DocFormat;
24  import org.kuali.ole.docstore.model.enums.DocType;
25  import org.kuali.ole.docstore.utility.ISBNUtil;
26  import org.kuali.ole.utility.Constants;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  import org.springframework.util.StopWatch;
30  
31  import java.io.IOException;
32  import java.text.DateFormat;
33  import java.text.Normalizer;
34  import java.text.ParseException;
35  import java.text.SimpleDateFormat;
36  import java.util.*;
37  import java.util.regex.Matcher;
38  import java.util.regex.Pattern;
39  
40  /**
41   * Created with IntelliJ IDEA.
42   * User: sambasivam
43   * Date: 12/17/13
44   * Time: 4:58 PM
45   * To change this template use File | Settings | File Templates.
46   */
47  public class BibMarcIndexer extends DocstoreSolrIndexService implements DocstoreConstants {
48  
49  
50      //    public static Map<String, String> FIELDS_TO_TAGS_2_INCLUDE_MAP = new HashMap<String, String>();
51  //    public static Map<String, String> FIELDS_TO_TAGS_2_EXCLUDE_MAP = new HashMap<String, String>();
52      private static final String SEPERATOR_DATA_FIELD = ", ";
53      private static final String SEPERATOR_SUB_FIELD = " ";
54      private static final String PATTERN_CHAR = "*";
55      private static final String SEPERATOR_HYPHEN = " - ";
56      private static final String SEPERATOR_DOUBLE_HYPHEN = " -- ";
57      private static final String DYNAMIC_FIELD_PREFIX = "mdf_";
58      private static final String BIB_IDENTIFIER = "bibIdentifier";
59      private static final String HOLDINGS_IDENTIFIER = "holdingsIdentifier";
60      private static final String ITEM_IDENTIFIER = "itemIdentifier";
61      private String publicationDateRegex = "[0-9]{4}";
62      private static final Logger LOG = LoggerFactory
63              .getLogger(BibMarcIndexer.class);
64  
65      private static BibMarcIndexer bibMarcIndexer = null;
66  
67      public static BibMarcRecordProcessor recordProcessor = new BibMarcRecordProcessor();
68      private static DocumentSearchConfig documentSearchConfig = null;
69  
70  //    static {
71  //        DocumentSearchConfig.getDocumentSearchConfig();
72  //        FIELDS_TO_TAGS_2_INCLUDE_MAP = Collections.unmodifiableMap(DocumentSearchConfig.FIELDS_TO_TAGS_2_INCLUDE_MAP);
73  //        FIELDS_TO_TAGS_2_EXCLUDE_MAP = Collections.unmodifiableMap(DocumentSearchConfig.FIELDS_TO_TAGS_2_EXCLUDE_MAP);
74  //    }
75  
76      public static BibMarcIndexer getInstance() {
77          if (bibMarcIndexer == null) {
78              bibMarcIndexer = new BibMarcIndexer();
79          }
80          documentSearchConfig = DocumentSearchConfig.getDocumentSearchConfig();
81          return bibMarcIndexer;
82      }
83  
84  
85      @Override
86      public void createTree(Object object) {
87          List<SolrInputDocument> solrInputDocuments = new ArrayList<>();
88          buildSolrDocsForBibTree((BibTree) object, solrInputDocuments);
89          indexSolrDocuments(solrInputDocuments, true);
90      }
91  
92      /**
93       * Build Solr input document for bib tree
94       *
95       * @param bibTree
96       * @param solrInputDocuments
97       */
98      private void buildSolrDocsForBibTree(BibTree bibTree, List<SolrInputDocument> solrInputDocuments) {
99          Bib bib = bibTree.getBib();
100         if (bib.getId() != null && bib.getId().contains("wbm")) {
101             BibMarcRecords bibMarcRecords = recordProcessor.fromXML(bib.getContent());
102             SolrInputDocument bibSolrDoc = buildSolrInputDocument(bibMarcRecords.getRecords().get(0));
103             setCommonFields(bib, bibSolrDoc);
104             solrInputDocuments.add(bibSolrDoc);
105             for (HoldingsTree holdingsTree : bibTree.getHoldingsTrees()) {
106                 if (holdingsTree.getHoldings() != null && holdingsTree.getHoldings().getOperation() != null && holdingsTree.getHoldings().getOperation().equals(DocstoreDocument.OperationType.NONE)) {
107                     continue;
108                 }
109                 buildSolrDocsForHoldingsTree(solrInputDocuments, bib, bibSolrDoc, holdingsTree);
110             }
111         }
112     }
113 
114 
115     /**
116      * Building Holdings Tree  Solr document
117      *
118      * @param solrInputDocuments
119      * @param bib
120      * @param bibSolrDoc
121      * @param holdingsTree
122      */
123     private void buildSolrDocsForHoldingsTree(List<SolrInputDocument> solrInputDocuments, Bib bib, SolrInputDocument bibSolrDoc, HoldingsTree holdingsTree) {
124         if (holdingsTree.getHoldings() != null) {
125             HoldingsOlemlIndexer holdingsOlemlIndexer = HoldingsOlemlIndexer.getInstance();
126             if (holdingsTree.getHoldings().getContent() != null || holdingsTree.getHoldings().getContentObject() != null) {
127                 SolrInputDocument holdingsSolrInputDoc = holdingsOlemlIndexer.getSolrInputFieldsForHoldings(holdingsTree.getHoldings());
128                 linkHoldingsWithBib(bibSolrDoc, holdingsSolrInputDoc, bib.getId(), solrInputDocuments, holdingsTree.getHoldings().getId());
129                 holdingsSolrInputDoc.addField(BIB_IDENTIFIER, bib.getId());
130                 List<Item> itemDocuments = holdingsTree.getItems();
131                 List<String> itemIds = new ArrayList<String>();
132                 holdingsSolrInputDoc.addField(ITEM_IDENTIFIER, itemIds);
133                 ItemOlemlIndexer itemOlemlIndexer = ItemOlemlIndexer.getInstance();
134                 addHoldingsInfoToBib(holdingsSolrInputDoc, bibSolrDoc);
135                 for (Item itemDocument : itemDocuments) {
136                     itemIds.add(itemDocument.getId());
137                     SolrInputDocument itemSolrInputDoc = itemOlemlIndexer.getSolrInputFieldsForItem(itemDocument);
138                     itemSolrInputDoc.addField(HOLDINGS_IDENTIFIER, holdingsTree.getHoldings().getId());
139                     itemSolrInputDoc.addField(BIB_IDENTIFIER, bib.getId());
140                     addBibInfoForHoldingsOrItems(itemSolrInputDoc, holdingsSolrInputDoc);
141                     addHoldingsInfoToItem(itemSolrInputDoc, holdingsSolrInputDoc);
142                     addItemInfoToBib(itemSolrInputDoc, bibSolrDoc);
143                     solrInputDocuments.add(itemSolrInputDoc);
144                 }
145 
146                 solrInputDocuments.add(holdingsSolrInputDoc);
147             } else if (StringUtils.isNotEmpty(holdingsTree.getHoldings().getId())) {
148                 bibSolrDoc.addField(HOLDINGS_IDENTIFIER, "who-" + holdingsTree.getHoldings().getId());
149             }
150         }
151     }
152 
153     private void linkHoldingsWithBib(SolrInputDocument bibSolrDoc, SolrInputDocument holdingsSolrInputDoc, String bibId, List<SolrInputDocument> solrInputDocuments, String holdingsId) {
154         if (bibSolrDoc == null) {
155             SolrDocument bibSolrDocument = getSolrDocumentByUUID(bibId);
156             bibSolrDoc = buildSolrInputDocFromSolrDoc(bibSolrDocument);
157             solrInputDocuments.add(bibSolrDoc);
158         }
159         bibSolrDoc.addField(HOLDINGS_IDENTIFIER, holdingsId);
160         addBibInfoForHoldingsOrItems(holdingsSolrInputDoc, bibSolrDoc);
161     }
162 
163     @Override
164     public void createTrees(Object object) {
165         BibTrees bibTreesObj = (BibTrees) object;
166         List<BibTree> bibTrees = bibTreesObj.getBibTrees();
167         List<SolrInputDocument> solrInputDocuments = new ArrayList<>();
168         for (BibTree bibTree : bibTrees) {
169             buildSolrDocsForBibTree(bibTree, solrInputDocuments);
170         }
171 
172         indexSolrDocuments(solrInputDocuments, true);
173     }
174 
175     /**
176      * process Bib Trees and commit once to solr.
177      * Delete documents and commit once
178      *
179      * @param bibTrees
180      */
181     @Override
182     public void processBibTrees(BibTrees bibTrees) {
183         List<SolrInputDocument> solrInputDocuments = new ArrayList<>();
184         List<String> idsToDelete = new ArrayList<>();
185         for (BibTree bibTree : bibTrees.getBibTrees()) {
186             processBibTree(bibTree, solrInputDocuments, idsToDelete);
187         }
188         LOG.info("Solr Input Documents Size : " + solrInputDocuments.size());
189         indexAndDelete(solrInputDocuments, idsToDelete, true);
190     }
191 
192     /**
193      * Process Bib tree
194      *
195      * @param bibTree
196      * @param solrInputDocuments
197      * @param idsToDelete
198      */
199     private void processBibTree(BibTree bibTree, List<SolrInputDocument> solrInputDocuments, List<String> idsToDelete) {
200         Bib bib = bibTree.getBib();
201         if (null != bib) {
202             SolrInputDocument bibSolrInputDocument = new SolrInputDocument();
203             if (Bib.ResultType.SUCCESS.equals(bib.getResult())) {
204                 if (bib.getId() != null) {
205                     if (Bib.OperationType.CREATE.equals(bib.getOperation())) {
206                         createBibTreeDocforSolr(bibTree, solrInputDocuments);
207                     } else if (Bib.OperationType.UPDATE.equals(bib.getOperation())) {
208                         updateBibDocument(bib, solrInputDocuments, bibSolrInputDocument);
209                         processHoldingsTrees(bibTree.getHoldingsTrees(), bibSolrInputDocument, solrInputDocuments, idsToDelete);
210                     } else if (Bib.OperationType.DELETE.equals(bib.getOperation())) {
211                         idsToDelete.add(bib.getId());
212                     }
213                 }
214             } else if (bib.getOperation() == null || StringUtils.isBlank(bib.getOperation().name())) {
215                 processHoldingsTrees(bibTree.getHoldingsTrees(), bibSolrInputDocument, solrInputDocuments, idsToDelete);
216             }
217         }
218     }
219 
220     /**
221      * @param bibTree
222      * @param solrInputDocuments
223      */
224     private void createBibTreeDocforSolr(BibTree bibTree, List<SolrInputDocument> solrInputDocuments) {
225         buildSolrDocsForBibTree(bibTree, solrInputDocuments);
226     }
227 
228     /**
229      * @param holdingsTrees
230      * @param bibSolrInputDocument
231      * @param solrInputDocuments
232      * @param idsToDelete
233      */
234     private void processHoldingsTrees(List<HoldingsTree> holdingsTrees, SolrInputDocument bibSolrInputDocument, List<SolrInputDocument> solrInputDocuments, List<String> idsToDelete) {
235         for (HoldingsTree holdingsTree : holdingsTrees) {
236             processHoldingsTree(holdingsTree, bibSolrInputDocument, solrInputDocuments, idsToDelete);
237         }
238     }
239 
240 
241     /**
242      * Proocess Holdings Tree and add Holdings tree  to solr input documents
243      *
244      * @param holdingsTree
245      * @param bibSolrInputDocument
246      * @param solrInputDocuments
247      * @param idsToDelete          *
248      */
249     private void processHoldingsTree(HoldingsTree holdingsTree, SolrInputDocument bibSolrInputDocument, List<SolrInputDocument> solrInputDocuments, List<String> idsToDelete) {
250         HoldingsOlemlIndexer holdingsOlemlIndexer = HoldingsOlemlIndexer.getInstance();
251         Holdings holdings = holdingsTree.getHoldings();
252         SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
253 
254         if (Holdings.ResultType.SUCCESS.equals(holdings.getResult())) {
255             if (holdings.getId() != null) {
256                 if (Holdings.OperationType.CREATE.equals(holdings.getOperation())) {
257                     Bib bib = holdings.getBib();
258                     if (null != bib && null != bib.getId()) {
259                         buildSolrDocsForHoldingsTree(solrInputDocuments, bib, bibSolrInputDocument, holdingsTree);
260                     }
261                 } else if (Holdings.OperationType.UPDATE.equals(holdings.getOperation())) {
262                     holdingsOlemlIndexer.processHoldingSolrDocumentForUpdate(holdings, solrInputDocuments, holdingsSolrInputDocument);
263                     processItems(holdingsTree.getItems(), solrInputDocuments, holdingsSolrInputDocument, idsToDelete);
264                 } else if (Holdings.OperationType.DELETE.equals(holdings.getOperation())) {
265                     idsToDelete.add(holdings.getId());
266                     for(Item item:holdingsTree.getItems()){
267                         idsToDelete.add(item.getId());
268                     }
269                     holdingsOlemlIndexer.processDelete(holdings.getId(), solrInputDocuments);
270                 }
271             }
272         } else if ((holdings.getOperation() == null || StringUtils.isBlank(holdings.getOperation().name()))) {
273             processItems(holdingsTree.getItems(), solrInputDocuments, holdingsSolrInputDocument, idsToDelete);
274         }
275 
276     }
277 
278 
279     /**
280      * Process items and add items to solr input documents
281      *
282      * @param items
283      * @param solrInputDocuments
284      * @param holdingsSolrInputDocument
285      * @param idsToDelete
286      */
287     private void processItems(List<Item> items, List<SolrInputDocument> solrInputDocuments, SolrInputDocument holdingsSolrInputDocument, List<String> idsToDelete) {
288         ItemOlemlIndexer itemOlemlIndexer = ItemOlemlIndexer.getInstance();
289         for (Item item : items) {
290             if (Item.ResultType.SUCCESS.equals(item.getResult())) {
291                 if (item.getId() != null) {
292                     if (Item.OperationType.CREATE.equals(item.getOperation())) {
293                         itemOlemlIndexer.buildSolrInputDocumentForBatchProcess(item, solrInputDocuments, holdingsSolrInputDocument);
294                     } else if (Item.OperationType.UPDATE.equals(item.getOperation())) {
295                         itemOlemlIndexer.updateRecordInSolr(item, solrInputDocuments);
296                     } else if (Item.OperationType.DELETE.equals(item.getOperation())) {
297                         idsToDelete.add(item.getId());
298                         itemOlemlIndexer.processDelete(item.getId(), solrInputDocuments);
299                     }
300                 }
301             }
302         }
303     }
304 
305     public void createTrees(Object object, ReindexBatchStatistics reindexBatchStatistics) {
306         BibTrees bibTreesObj = (BibTrees) object;
307         List<BibTree> bibTrees = bibTreesObj.getBibTrees();
308         List<SolrInputDocument> solrInputDocuments = new ArrayList<>();
309         StopWatch stopWatch = new StopWatch();
310 
311         stopWatch.start();
312         for (BibTree bibTree : bibTrees) {
313             buildSolrDocsForBibTree(bibTree, solrInputDocuments);
314         }
315         stopWatch.stop();
316         reindexBatchStatistics.addBuildSolrDocsTime(stopWatch.getTotalTimeMillis());
317 
318         indexSolrDocuments(solrInputDocuments, true, reindexBatchStatistics);
319     }
320 
321     protected void indexSolrDocuments(List<SolrInputDocument> solrDocs, boolean isCommit, ReindexBatchStatistics reindexBatchStatistics) {
322         SolrServer solr = null;
323         try {
324             solr = SolrServerManager.getInstance().getSolrServer();
325             StopWatch stopWatch = new StopWatch();
326             stopWatch.start("add");
327             UpdateResponse response = solr.add(solrDocs);
328             stopWatch.stop();
329             reindexBatchStatistics.addRecToAddInSolr(stopWatch.getLastTaskTimeMillis());
330             if (isCommit) {
331                 stopWatch.start("commit");
332                 solr.commit(false, false);
333                 stopWatch.stop();
334                 reindexBatchStatistics.addCommitTime(stopWatch.getLastTaskTimeMillis());
335             }
336         } catch (SolrServerException e) {
337             LOG.info("Exception :", e);
338             rollback(solr);
339             throw new DocstoreIndexException(e.getMessage());
340         } catch (IOException e) {
341             LOG.info("Exception :", e);
342             rollback(solr);
343             throw new DocstoreIndexException(e.getMessage());
344         }
345     }
346 
347 
348     protected void buildSolrInputDocument(Object object, List<SolrInputDocument> solrInputDocuments) {
349         Bib bib = (Bib) object;
350         BibMarcRecords bibMarcRecords = recordProcessor.fromXML(bib.getContent());
351         SolrInputDocument solrInputDocument = buildSolrInputDocument(bibMarcRecords.getRecords().get(0));
352 
353         setCommonFields(bib, solrInputDocument);
354 
355         solrInputDocuments.add(solrInputDocument);
356 
357     }
358 
359     protected void setCommonFields(Bib bib, SolrInputDocument solrInputDocument) {
360         solrInputDocument.setField(ID, bib.getId());
361         solrInputDocument.addField(LOCALID_SEARCH, DocumentLocalId.getDocumentId(bib.getId()));
362         solrInputDocument.addField(LOCALID_DISPLAY, DocumentLocalId.getDocumentIdDisplay(bib.getId()));
363         solrInputDocument.addField(UNIQUE_ID, bib.getId());
364         solrInputDocument.setField(DOC_CATEGORY, DocCategory.WORK.getCode());
365         solrInputDocument.setField(BIB_ID, bib.getId());
366 
367         solrInputDocument.setField(STATUS_SEARCH, bib.getStatus());
368         solrInputDocument.setField(STATUS_DISPLAY, bib.getStatus());
369 
370         if (StringUtils.isNotEmpty(bib.getStatusUpdatedOn())) {
371             solrInputDocument.setField(STATUS_UPDATED_ON, getDate(bib.getStatusUpdatedOn()));
372         }
373 
374         solrInputDocument.addField(STAFF_ONLY_FLAG, bib.isStaffOnly());
375 
376         String createdBy = bib.getCreatedBy();
377         solrInputDocument.setField(CREATED_BY, createdBy);
378         solrInputDocument.setField(UPDATED_BY, createdBy);
379 
380         Date date = new Date();
381         Date createdDate = null;
382 
383         if (StringUtils.isNotBlank(bib.getCreatedOn())) {
384             createdDate = getDate(bib.getCreatedOn());
385             solrInputDocument.setField(DATE_ENTERED, createdDate);
386         } else {
387             solrInputDocument.setField(DATE_ENTERED, date);
388         }
389 
390         if (StringUtils.isNotBlank(bib.getUpdatedOn())) {
391             solrInputDocument.setField(DATE_UPDATED, getDate(bib.getUpdatedOn()));
392         } else {
393             if (StringUtils.isNotBlank(bib.getCreatedOn())) {
394                 // Updated date will have created date value when bib is not updated after it is created.
395                 solrInputDocument.setField(DATE_UPDATED, createdDate);
396             } else {
397                 solrInputDocument.setField(DATE_UPDATED, date);
398             }
399         }
400     }
401 
402     protected void updateRecordInSolr(Object object, List<SolrInputDocument> solrInputDocuments) {
403         Bib bib = (Bib) object;
404         List<SolrDocument> solrDocumentList = getSolrDocumentBySolrId(bib.getId());
405         SolrDocument solrDocument = solrDocumentList.get(0);
406         SolrInputDocument solrInputDocument = new SolrInputDocument();
407         if (bib.getContent() != null) {
408             BibMarcRecord workBibMarcRecord = recordProcessor.fromXML(bib.getContent()).getRecords().get(0);
409             solrInputDocument = buildSolrInputDocument(workBibMarcRecord);
410             if (solrDocument != null && solrDocument.getFieldValue(HOLDINGS_IDENTIFIER) != null) {
411                 addBibInfoToHoldings(solrInputDocuments, solrInputDocument, solrDocument);
412             }
413             if (StringUtils.isNotEmpty(bib.getStatusUpdatedOn())) {
414                 solrInputDocument.setField(STATUS_UPDATED_ON, getDate(bib.getStatusUpdatedOn()));
415             }
416         } else {
417             buildSolrInputDocFromSolrDoc(solrDocument, solrInputDocument);
418         }
419         setCommonFieldsForSolrDoc(solrInputDocument, bib, solrDocument);
420         solrInputDocuments.add(solrInputDocument);
421     }
422 
423     /**
424      * Updating Bib solr document with holding and other details
425      *
426      * @param object
427      * @param solrInputDocuments
428      * @param solrbibInputDocument
429      */
430     protected void updateBibDocument(Object object, List<SolrInputDocument> solrInputDocuments, SolrInputDocument solrbibInputDocument) {
431         Bib bib = (Bib) object;
432         List<SolrDocument> solrDocumentList = getSolrDocumentBySolrId(bib.getId());
433         SolrDocument solrDocument = solrDocumentList.get(0);
434 
435         if (bib.getContent() != null) {
436             BibMarcRecord workBibMarcRecord = recordProcessor.fromXML(bib.getContent()).getRecords().get(0);
437             solrbibInputDocument = buildSolrInputDocument(workBibMarcRecord, solrbibInputDocument);
438 
439             if (solrDocument != null && solrDocument.getFieldValue(HOLDINGS_IDENTIFIER) != null) {
440                 addBibInfoToHoldings(solrInputDocuments, solrbibInputDocument, solrDocument);
441             }
442             if (StringUtils.isNotEmpty(bib.getStatus()) || StringUtils.isNotEmpty(bib.getStatusUpdatedOn())) {
443                 solrbibInputDocument.setField(STATUS_UPDATED_ON, getDate(bib.getStatusUpdatedOn()));
444             }
445         } else {
446             buildSolrInputDocFromSolrDoc(solrDocument, solrbibInputDocument);
447         }
448         setCommonFieldsForSolrDoc(solrbibInputDocument, bib, solrDocument);
449         solrInputDocuments.add(solrbibInputDocument);
450 
451     }
452 
453     private void addBibInfoToHoldings(List<SolrInputDocument> solrInputDocuments, SolrInputDocument bibSolrDoc, SolrDocument solrDocument) {
454         Object instanceIdentifier = solrDocument.getFieldValue(HOLDINGS_IDENTIFIER);
455         bibSolrDoc.addField(HOLDINGS_IDENTIFIER, instanceIdentifier);
456         List<String> holdinsgsIds = new ArrayList<>();
457         if (instanceIdentifier instanceof String) {
458             holdinsgsIds.add((String) instanceIdentifier);
459         } else {
460             holdinsgsIds.addAll((List<String>) instanceIdentifier);
461         }
462 
463         for (String holdingsId : holdinsgsIds) {
464             List<SolrDocument> solrDocumentList = getSolrDocumentBySolrId(holdingsId);
465             if (CollectionUtils.isNotEmpty(solrDocumentList)) {
466                 SolrDocument holdingsSolrDocument = solrDocumentList.get(0);
467                 SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
468                 buildSolrInputDocFromSolrDoc(holdingsSolrDocument, holdingsSolrInputDocument);
469                 removeFieldFromSolrInputDocument(holdingsSolrInputDocument);
470                 addBibInfoForHoldingsOrItems(holdingsSolrInputDocument, bibSolrDoc);
471                 List<String> itemIds = new ArrayList<>();
472 
473                 Object itemIdentifier = holdingsSolrDocument.getFieldValue(ITEM_IDENTIFIER);
474                 if (itemIdentifier != null) {
475                     if (itemIdentifier instanceof String) {
476                         itemIds.add((String) itemIdentifier);
477                     } else {
478                         itemIds.addAll((List<String>) itemIdentifier);
479                     }
480                 }
481 
482                 for (String itemId : itemIds) {
483 
484                     List<SolrDocument> itemDocumentList = getSolrDocumentBySolrId(itemId);
485                     SolrDocument itemSolrDocument = itemDocumentList.get(0);
486                     SolrInputDocument itemSolrInputDocument = new SolrInputDocument();
487                     buildSolrInputDocFromSolrDoc(itemSolrDocument, itemSolrInputDocument);
488                     removeFieldFromSolrInputDocument(itemSolrInputDocument);
489                     addBibInfoForHoldingsOrItems(itemSolrInputDocument, bibSolrDoc);
490                     addHoldingsInfoToItem(itemSolrInputDocument, bibSolrDoc);
491                     solrInputDocuments.add(itemSolrInputDocument);
492                 }
493                 solrInputDocuments.add(holdingsSolrInputDocument);
494 
495             }
496         }
497 
498     }
499 
500     protected void deleteRecordInSolr(SolrServer solrServer, String id) throws IOException, SolrServerException {
501         String query = "bibIdentifier:" + id + " OR " + "id:" + id;
502         UpdateResponse updateResponse = solrServer.deleteByQuery(query);
503         LOG.info("updateResponse " + updateResponse);
504         // save deleted  Bibliographic with Bibliographic_delete
505         String newId = id + "_d";
506         SolrInputDocument solrInputDocument = new SolrInputDocument();
507         solrInputDocument.setField("DocType", "bibliographic_delete");
508         solrInputDocument.setField("dateUpdated", new Date());
509         solrInputDocument.setField("uniqueId", newId);
510         solrInputDocument.setField("id", newId);
511         solrInputDocument.setField("LocalId_display", DocumentLocalId.getDocumentIdDisplay(id));
512         UpdateResponse updateResponseForBib = solrServer.add(solrInputDocument);
513         LOG.debug("updateResponse " + updateResponseForBib);
514 
515     }
516 
517     private void setCommonFieldsForSolrDoc(SolrInputDocument solrInputDocument, Bib bib, SolrDocument solrDocument) {
518         solrInputDocument.setField(ID, bib.getId());
519         solrInputDocument.addField(UNIQUE_ID, bib.getId());
520         solrInputDocument.setField(DOC_CATEGORY, DocCategory.WORK.getCode());
521         String updatedBy = bib.getUpdatedBy();
522         solrInputDocument.setField(UPDATED_BY, updatedBy);
523         solrInputDocument.setField(DATE_UPDATED, new Date());
524         solrInputDocument.setField(CREATED_BY, solrDocument.getFieldValue(CREATED_BY));
525         solrInputDocument.setField(DATE_ENTERED, solrDocument.getFieldValue(DATE_ENTERED));
526         solrInputDocument.setField(BIB_ID, bib.getId());
527         solrInputDocument.addField(LOCALID_SEARCH, DocumentLocalId.getDocumentId(bib.getId()));
528         solrInputDocument.addField(LOCALID_DISPLAY, DocumentLocalId.getDocumentIdDisplay(bib.getId()));
529         solrInputDocument.addField(STAFF_ONLY_FLAG, bib.isStaffOnly());
530         solrInputDocument.setField(STATUS_SEARCH, bib.getStatus());
531         solrInputDocument.setField(STATUS_DISPLAY, bib.getStatus());
532     }
533 
534 
535     private Date getDate(String dateStr) {
536         DateFormat format = new SimpleDateFormat(Constants.DATE_FORMAT);
537         try {
538             if (StringUtils.isNotEmpty(dateStr)) {
539                 return format.parse(dateStr);
540             } else {
541                 return new Date();
542             }
543 
544         } catch (ParseException e) {
545             LOG.info("Exception : " + dateStr + " for format:: " + Constants.DATE_FORMAT, e);
546             return new Date();
547         }
548     }
549 
550 
551     /**
552      * Method to build Solr Input Document from a given Work Bib Marc Record
553      *
554      * @param record
555      * @return
556      */
557     public SolrInputDocument buildSolrInputDocument(BibMarcRecord record) {
558         SolrInputDocument solrDoc = new SolrInputDocument();
559         buildSolrInputDocument(record, solrDoc);
560         return solrDoc;
561     }
562 
563     public SolrInputDocument buildSolrInputDocument(BibMarcRecord record, SolrInputDocument solrDoc) {
564 
565         solrDoc.addField(LEADER, record.getLeader());
566 
567         // Title Field Calculations.
568         List<ControlField> controlFieldList = record.getControlFields();
569 
570         for (ControlField cf : controlFieldList) {
571             solrDoc.addField("controlfield_" + cf.getTag(), cf.getValue());
572         }
573 
574         solrDoc.addField(DOC_TYPE, DocType.BIB.getDescription());
575         solrDoc.addField(DOC_FORMAT, DocFormat.MARC.getDescription());
576 
577         for (String field : documentSearchConfig.FIELDS_TO_TAGS_2_INCLUDE_MAP.keySet()) {
578             Object object = buildFieldValue(field, record);
579             if(object != null){
580                 addFieldToSolrDoc(record, field,object, solrDoc);
581             }
582 
583         }
584         addFieldToSolrDoc(record, ALL_TEXT, getAllText(record), solrDoc);
585         addGeneralFieldsToSolrDoc(record, solrDoc);
586         if (record.getLeader() == null || ((record.getLeader().length() >= 8) && (record.getLeader().charAt(7) != 's'))) {
587             solrDoc.removeField(JOURNAL_TITLE_SEARCH);
588             solrDoc.removeField(JOURNAL_TITLE_DISPLAY);
589             solrDoc.removeField(JOURNAL_TITLE_SORT);
590         }
591         addFieldToSolrDoc(record, PUBLISHER_SORT, solrDoc.getFieldValue(PUBLISHER_SEARCH), solrDoc);
592         return solrDoc;
593     }
594 
595     private void addFieldToSolrDoc(BibMarcRecord record, String fieldName, Object value,
596                                    SolrInputDocument solrDoc) {
597         int ind2Value = 0;
598         if (value instanceof List) {
599             if (fieldName.toLowerCase().endsWith("_sort")) // Sort fields only the first value to be inserted.
600             {
601                 ind2Value = getSecondIndicator(record, fieldName);
602                 LOG.debug("field name -->" + fieldName + "----->" + ind2Value);
603                 if (ind2Value > 0) {
604                     solrDoc.addField(fieldName, ((List) value).get(0).toString().substring(ind2Value));
605                 } else {
606                     solrDoc.addField(fieldName, ((List) value).get(0));
607                 }
608 
609             } else if (fieldName.endsWith("_facet")) {
610                 solrDoc.addField(fieldName, getSortString((List) value));
611             } else {
612                 if (((List) value).size() > 0) {
613                     for (Object obj : (List<Object>) value)
614                     // All non Sort and Multi Valued Fields
615                     {
616                         solrDoc.addField(fieldName, obj);
617                     }
618                 } else {
619                     solrDoc.addField(fieldName, null);
620                 }
621             }
622         } else {
623             if (fieldName.toLowerCase().endsWith("_sort")) // Sort fields only the first value to be inserted.
624             {
625                 ind2Value = getSecondIndicator(record, fieldName);
626                 LOG.debug("field name -->" + fieldName + "----->" + ind2Value);
627                 if (value != null && ind2Value > 0) {
628                     String fieldValue = value.toString();
629                     try {
630                         fieldValue = value.toString().substring(ind2Value);
631                     } catch (Exception e) {
632                         LOG.error("Exception while getting value:" + value.toString() + " for field:" + fieldName + ". Exception:" + e.toString());
633                         // TODO: log the exception trace here.
634                     }
635                     solrDoc.addField(fieldName, fieldValue);
636                 } else {
637                     solrDoc.addField(fieldName, value);
638                 }
639             } else if (fieldName.endsWith("_facet")) {
640                 if (value != null) {
641                     solrDoc.addField(fieldName, getSortString(value.toString()));
642                 }
643             } else {
644                 solrDoc.addField(fieldName, value);
645             }
646         }
647     }
648 
649     /**
650      * Method to build Field Value for a given field Name and given record.
651      *
652      * @param fieldName - field name should be one of the defined names in WorkBibMarcDocBuilder
653      * @param record    - WorkBibMarcRecord
654      * @return - returns the field value build over from the given record.
655      */
656     public Object buildFieldValue(String fieldName, BibMarcRecord record) {
657         List<ControlField> controlFieldList = record.getControlFields();
658         List<DataField> dataFields = record.getDataFields();
659         String includeTags = documentSearchConfig.FIELDS_TO_TAGS_2_INCLUDE_MAP.get(fieldName);
660         if ((includeTags != null) && (includeTags.length() > 0)) {
661             String excludeTags = documentSearchConfig.FIELDS_TO_TAGS_2_EXCLUDE_MAP.get(fieldName);
662             if (excludeTags == null) {
663                 excludeTags = "";
664             }
665             if (fieldName.startsWith("Subject_")) {
666                 return getDataFieldValue(includeTags, excludeTags, record, true, fieldName);
667             } else {
668                 if (fieldName.equals(ISBN_SEARCH))
669                     return normalizeIsbn(getDataFieldValue(includeTags, excludeTags, record, false, fieldName));
670                 else
671                     return getDataFieldValue(includeTags, excludeTags, record, false, fieldName);
672             }
673         } else if (fieldName.equals(PUBLICATIONDATE_DISPLAY) || fieldName.equals(PUBLICATIONDATE_SEARCH) || fieldName.equals(PUBLICATIONDATE_FACET)
674                 || fieldName.equals(PUBLICATIONDATE_SORT)) {
675             String publicationDate = "";
676             String publicationEndDate = "";
677             Object publicationDateValue = null;
678             for (ControlField controlField : controlFieldList) {
679                 if (controlField.getTag().equalsIgnoreCase("008")) {
680                     String controlField008 = controlField.getValue();
681                     if (controlField008 != null && controlField008.length() > 10) {
682                         publicationDate = controlField008.substring(7, 11);
683                         publicationDate = extractPublicationDateWithRegex(publicationDate);
684                         if (controlField008.length() > 14) {
685                             publicationEndDate = controlField008.substring(11, 15);
686                             publicationEndDate = extractPublicationDateWithRegex(publicationEndDate);
687                         }
688                     }
689                 }
690             }
691             if (publicationDate == null || publicationDate.trim().length() == 0) {
692                 if (getDataFieldValue("260-c", "", record, true, fieldName) instanceof String) {
693                     publicationDate = (String) getDataFieldValue("260-c", "", record, true, fieldName);
694                 } else if (getDataFieldValue("260-c", "", record, true, fieldName) instanceof List) {
695                     publicationDate = ((List<String>) getDataFieldValue("260-c", "", record, true, fieldName)).get(0);
696                 }
697                 publicationDate = extractPublicationDateWithRegex(publicationDate);
698             }
699             if (fieldName.equals(PUBLICATIONDATE_FACET)) {
700                 if (publicationDate.equalsIgnoreCase("")) {
701                     publicationDateValue = "Date could not be determined";
702                 } else {
703                     publicationDateValue = buildPublicationDateFacetValue(publicationDate, publicationEndDate);
704                 }
705                 return publicationDateValue;
706             }
707             return publicationDate;
708         } else if (fieldName.equals(LANGUAGE_DISPLAY) || fieldName.equals(LANGUAGE_SEARCH) || fieldName.equals(LANGUAGE_FACET)) {
709             List<Object> langs = new ArrayList<Object>();
710             for (ControlField controlField : controlFieldList) {
711                 if (controlField.getTag().equalsIgnoreCase("008")) {
712                     String cf8 = controlField.getValue();
713                     if (cf8 != null && cf8.length() > 37) {
714                         String lang = Languages.getInstance(Languages.ISO_639_3).getLanguageDescription(
715                                 cf8.substring(35, 38));
716                         langs.add(lang == null ? "Undefined" : lang);
717                     }
718                 }
719             }
720             if (fieldName.equals(LANGUAGE_SEARCH) || fieldName.equals(LANGUAGE_FACET)) {
721                 for (DataField df : dataFields) {
722                     if (df.getTag().equals("546")) {
723                         try {
724                             for (SubField subfield : df.getSubFields()) {
725                                 if (subfield.getCode().equalsIgnoreCase("a")) {
726                                     langs.add(subfield.getValue());
727                                 }
728                             }
729                         } catch (RuntimeException re) {
730                             LOG.info("Exception :", re);
731                         }
732                     }
733                 }
734             }
735             return langs;
736         } else if (fieldName.equals(FORMAT_DISPLAY) || fieldName.equals(FORMAT_SEARCH) || fieldName.equals(FORMAT_FACET)) {
737             return getRecordFormat(record);
738         }  else if (fieldName.equals(RESOURCETYPE_DISPLAY) || fieldName.equals(RESOURCETYPE_SEARCH)) {
739             return getRecordFormat_ResourceType(record);
740         }  else if (fieldName.equals(CARRIER_DISPLAY) || fieldName.equals(CARRIER_SEARCH)) {
741             return getRecordFormat_Carrier(record);
742         } else if(fieldName.equals(DESCRIPTION_SEARCH)) {
743             String excludeTags = documentSearchConfig.FIELDS_TO_TAGS_2_EXCLUDE_MAP.get(fieldName);
744             if (excludeTags == null) {
745                 excludeTags = "";
746             }
747             if (includeTags == null) {
748                 includeTags = "";
749             }
750             return getDataFieldValue(includeTags, excludeTags, record, false, fieldName);
751         } else {
752             return null;
753         }
754     }
755 
756     /**
757      * Method to give all_text field to a given record.
758      *
759      * @param record
760      * @return
761      */
762     public String getAllText(BibMarcRecord record) {
763         StringBuilder allText = new StringBuilder();
764         allText.append(record.getLeader());
765         allText.append(SEPERATOR_DATA_FIELD);
766         for (ControlField cf : record.getControlFields()) {
767             allText.append(cf.getValue());
768             allText.append(SEPERATOR_DATA_FIELD);
769         }
770         for (DataField df : record.getDataFields()) {
771             for (SubField sf : df.getSubFields()) {
772                 allText.append(sf.getValue());
773                 allText.append(SEPERATOR_SUB_FIELD);
774                 if(sf.getValue().contains("-")){
775                     allText.append(sf.getValue().replace("-",""));
776                     allText.append(SEPERATOR_SUB_FIELD);
777                 }
778             }
779             allText.append(SEPERATOR_DATA_FIELD);
780         }
781         return allText.toString();
782     }
783 
784     /**
785      * Method to get Record Format.
786      *
787      * @param record
788      * @return
789      */
790     public String getRecordFormat(BibMarcRecord record) {
791         String format = null;
792         String cF7 = null;
793         String cF8 = null;
794         String formatData = "";
795         char cF8Ch21 = ' ';
796         char cF8Ch22 = ' ';
797         char cF8Ch28 = ' ';
798         char cF7Ch0 = ' ';
799         int cFIndex = record.getControlFields().indexOf(new ControlField("007"));
800         if (cFIndex != -1) {
801             cF7 = record.getControlFields().get(cFIndex).getValue();
802         }
803         cFIndex = record.getControlFields().indexOf(new ControlField("008"));
804         if (cFIndex != -1) {
805             cF8 = record.getControlFields().get(cFIndex).getValue();
806         }
807         Object tmp = null;
808         tmp = getDataFieldValue("111-a", "", record, false, "");
809         String dF111a = tmp != null ? tmp.toString() : null;
810         tmp = getDataFieldValue("254-h", "", record, false, "");
811         String dF254h = tmp != null ? tmp.toString() : null;
812         tmp = getDataFieldValue("254-k", "", record, false, "");
813         String dF254k = tmp != null ? tmp.toString() : null;
814         tmp = getDataFieldValue("260-b", "", record, false, "");
815         String dF260b = tmp != null ? tmp.toString() : null;
816         tmp = getDataFieldValue("502-a", "", record, false, "");
817         String dF502a = tmp != null ? tmp.toString() : null;
818         tmp = getDataFieldValue("711-a", "", record, false, "");
819         String dF711a = tmp != null ? tmp.toString() : null;
820 
821         if (cF8 != null && cF8.length() > 22) {
822             cF8Ch21 = cF8.charAt(21);
823             cF8Ch22 = cF8.charAt(22);
824         }
825         if (cF8 != null && cF8.length() > 28) {
826             cF8Ch28 = cF8.charAt(28);
827         }
828         if (cF7 != null) {
829             cF7Ch0 = cF7.charAt(0);
830         }
831         if (record.getLeader() != null && record.getLeader().length() > 8) {
832             formatData = record.getLeader().substring(6, 8);
833         }
834 
835         if (dF254h != null && dF254h.contains("micro")) {
836             format = "Microformat";
837         } else if (formatData.equals("tm") && dF502a != null) {
838             format = "Thesis/Dissertation";
839         } else if (dF111a != null || dF711a != null) {
840             format = "Conference/Event";
841         } else if (formatData.equals("aa") || formatData.equals("am") || formatData.equals("ac") || formatData
842                 .equals("tm")) {
843             if (dF254k != null && dF254k.contains("kit")) {
844                 format = "Other";
845             } else {
846                 format = "Book";
847             }
848         } else if (formatData.equals("im") || formatData.equals("jm") || formatData.equals("jc")
849                 || formatData.equals("jd") || formatData.equals("js")) {
850             format = "Sound recording";
851         } else if (formatData.equals("cm") || formatData.equals("dm") || formatData.equals("ca")
852                 || formatData.equals("cb") || formatData.equals("cd") || formatData.equals("cs")) {
853             format = "Musical score";
854         } else if (formatData.equals("fm") || ("".equals(formatData) && formatData.startsWith("e"))) {
855             format = "Map/Atlas";
856         } else if (formatData.equals("gm") || (cF7 != null && (cF7Ch0 == ('v')))) {
857             format = "Video";
858         } else if (formatData.equals("gm") || (cF7 != null && (cF7Ch0 == ('g')))) {
859             format = "Projected graphic";
860         } else if (formatData.equals("as") || formatData.equals("gs")) {
861             format = "Journal/Periodical";
862         } else if (formatData.equals("km")) {
863             format = "Image";
864         } else if (formatData.equals("mm")) {
865             format = "Datafile";
866         } else if (formatData.equals("as") && (cF8Ch21 == 'n' || cF8Ch22 == 'e')) {
867             format = "Newspaper";
868         } else if ("".equals(formatData) && formatData.startsWith("r")) {
869             format = "3D object";
870         } else if (formatData != "" && formatData.endsWith("i")) {
871             format = "Database/Website";
872         } else if (("".equals(formatData) && (!formatData.startsWith("c") || !formatData.startsWith("d")
873                 || !formatData.startsWith("i") || !formatData.startsWith("j"))) && (
874                 (cF8Ch28 == 'f' || cF8Ch28 == 'i' || cF8Ch28 == 'o') && (dF260b != null && !dF260b
875                         .contains("press")))) {
876             format = "Government document";
877         } else {
878             format = "Other";
879         }
880         return format;
881     }
882 
883     /**
884      * Method to get Record Format.
885      *
886      * @param record
887      * @return
888      */
889     public String getRecordFormat_ResourceType(BibMarcRecord record) {
890         String format = null;
891         char leader6 = ' ';
892         char leader7 = ' ';
893         if (record.getLeader() != null) {
894             String leader = record.getLeader().trim();
895             if (StringUtils.isNotBlank(leader)) {
896                 if (leader.length() >= 7) {
897                     leader6 = leader.charAt(6);
898                 }
899                 if (leader.length() >= 8) {
900                     leader7 = leader.charAt(7);
901                 }
902 
903                 if ((leader6 == 'a' || leader6 == 't') && leader7 == 'm') {
904                     format = "Book";
905                 }
906                 if (leader6 == 'a' && leader7 == 's') {
907                     format = "Serial";
908                 }
909                 if (leader6 == 'c' || leader6 == 'd') {
910                     format = "Score";
911                 }
912                 if (leader6 == 'j' || leader6 == 'i') {
913                     format = "Sound recording";
914                 }
915                 if (leader6 == 'e' || leader6 == 'f') {
916                     format = "Map";
917                 }
918                 if (leader6 == 'g') {
919                     format = "Motion picture";
920                 }
921                 if (leader6 == 'k') {
922                     format = "Photo/Print";
923                 }
924                 if (leader6 == 'm') {
925                     format = "Computer file";
926                 }
927                 if (leader6 == 'p') {
928                     format = "Archival materials";
929                 }
930                 if (leader6 == 'r') {
931                     format = "Artifacts";
932                 }
933             }
934         }
935         return format;
936     }
937 
938     public String getRecordFormat_Carrier(BibMarcRecord record) {
939         String format = null;
940         String cF7 = null;
941         String cF8 = null;
942         char cF70 = ' ';
943         char cF71 = ' ';
944         char cF823 = ' ';
945         char cF829 = ' ';
946         char leader06 = ' ';
947         String leader ="";
948         if (record.getLeader() != null) {
949             leader = record.getLeader();
950         }
951         for (ControlField controlField : record.getControlFields()) {
952             if (controlField.getTag().equals("007")) {
953                 cF7 = controlField.getValue();
954             }else if(controlField.getTag().equals("008")){
955                 cF8 = controlField.getValue();
956             }
957         }
958 
959         if(StringUtils.isNotBlank(cF7) && cF7.length() >= 1){
960             cF70 = cF7.charAt(0);
961         }
962         if(StringUtils.isNotBlank(cF7) && cF7.length() >= 2){
963             cF71 = cF7.charAt(1);
964         }
965         if(StringUtils.isNotBlank(cF8) && cF8.length() >= 24){
966             cF823 = cF8.charAt(23);
967         }
968         if(StringUtils.isNotBlank(cF8) && cF8.length() >= 30){
969             cF829 = cF8.charAt(29);
970         }
971         if(StringUtils.isNotBlank(leader) && leader.length() >= 7){
972             leader06 = leader.charAt(6);
973         }
974 
975         if(cF70 == 'h'){
976             format = "Microform";
977             return format;
978         }
979         if(cF70 == 'c' && cF71 == 'r'){
980             format = "Remote e-resource";
981             return format;
982         }
983         if(cF70 == 'c' && cF71 != 'r'){
984             format = "Direct access 3-resource";
985             return format;
986         }
987         if((leader06 == 'a' || leader06 == 'c' || leader06 == 'd' || leader06 == 'p' || leader06 == 't') && (cF823 == 'd' || cF823 == 'f' || cF823 == 'r' || cF823 == ' ')){
988             format = "Print";
989         }
990         if((leader06 == 'e' || leader06 == 'f' || leader06 == 'k') && (cF829 == 'd' || cF829 == 'r' || cF829 == ' ')){
991             format = "Print";
992         }
993         return format;
994     }
995 
996     /**
997      * Method to give data field value of a given include tags and exclude tags
998      *
999      * @param includeTags
1000      * @param excludeTags
1001      * @param record
1002      * @param isHyphenSeperatorFirst - Pass 'false' by default (if it is not a subject field (Currently)).
1003      *                               - Pass 'true' if it has to get encoded first subfield values with " - ".
1004      * @param fieldName
1005      * @return
1006      */
1007     private Object getDataFieldValue(String includeTags, String excludeTags, BibMarcRecord record,
1008                                      boolean isHyphenSeperatorFirst, String fieldName) {
1009         List<Object> fieldValues = new ArrayList<Object>();
1010         StringTokenizer includeTagsTokenizer = new StringTokenizer(includeTags, ",");
1011 
1012         while (includeTagsTokenizer.hasMoreElements()) {
1013             String tag = includeTagsTokenizer.nextToken();
1014             tag = tag.trim();
1015             int subFieldIdx = tag.indexOf('-');
1016             String tagNum = (subFieldIdx == -1) ? tag : tag.substring(0, subFieldIdx);
1017 
1018             for (int i = 0; i < record.getDataFields().size(); i++) {
1019                 DataField dataField = record.getDataFields().get(i);
1020                 if (isValidTag(dataField.getTag(), tagNum)) {
1021                     StringBuilder fieldValue = new StringBuilder();
1022                     List<SubField> subFields = dataField.getSubFields();
1023                     if (subFieldIdx != -1) { // Includes only one Sub Field of a main Data Field.
1024                         if (!excludeTags.contains(tag)) {
1025                             String subFieldCodes = tag.substring(subFieldIdx + 1, tag.length());
1026                             boolean isHyphenCodedOnce = false;
1027                             for (SubField subField : subFields) {
1028                                 if (subFieldCodes.contains(subField.getCode())) {
1029                                     if (fieldValue.length() != 0) {
1030                                         if (!isHyphenSeperatorFirst || isHyphenCodedOnce || (
1031                                                 dataField.getTag().endsWith("00") || dataField.getTag().endsWith("10")
1032                                                         || dataField.getTag().endsWith("11"))) {
1033                                             fieldValue.append(SEPERATOR_SUB_FIELD);
1034                                         } else {
1035                                             fieldValue.append(SEPERATOR_HYPHEN);
1036                                             isHyphenCodedOnce = true;
1037                                         }
1038                                     }
1039                                     fieldValue.append(subField.getValue());
1040                                 }
1041                             }
1042                         }
1043                     } else { // Includes whole Data Field i.e includes All Sub Fields in a datafield
1044                         boolean isHyphenCodedOnce = false;
1045                         boolean isFirstSubField = false;
1046                         for (SubField subField : subFields) {
1047                             if (!excludeTags.contains(dataField.getTag() + "-" + subField.getCode()) && !excludeTags
1048                                     .contains(tagNum + "-" + subField.getCode())) {
1049                                 if (fieldValue.length() != 0) {
1050                                     if (!isHyphenSeperatorFirst || isHyphenCodedOnce || (
1051                                             dataField.getTag().endsWith("00") || dataField.getTag().endsWith("10")
1052                                                     || dataField.getTag().endsWith("11"))) {
1053                                         fieldValue.append(SEPERATOR_SUB_FIELD);
1054                                     } else if (fieldName != null && (fieldName.equalsIgnoreCase(SUBJECT_FACET)
1055                                             || fieldName.equalsIgnoreCase(SUBJECT_DISPLAY))) {
1056                                         if (dataField.getTag().equalsIgnoreCase("630")) {
1057                                             if (subField.getCode().equals("v") || subField.getCode().equals("x")
1058                                                     || subField.getCode().equals("y") || subField.getCode().equals("z")) {
1059                                                 fieldValue.append(SEPERATOR_DOUBLE_HYPHEN);
1060                                             }
1061                                         } else if (dataField.getTag().equalsIgnoreCase("650") || dataField.getTag()
1062                                                 .equalsIgnoreCase(
1063                                                         "651")) {
1064                                             if (isFirstSubField && fieldName.equalsIgnoreCase(SUBJECT_FACET)) {
1065                                                 fieldValues.add(fieldValue.toString().trim());
1066                                             }
1067                                             fieldValue.append(SEPERATOR_DOUBLE_HYPHEN);
1068                                             isFirstSubField = true;
1069                                         } else {
1070                                             fieldValue.append(SEPERATOR_SUB_FIELD);
1071                                         }
1072                                     } else {
1073                                         if (fieldName.startsWith("Subject_")) {
1074                                             fieldValue.append(SEPERATOR_SUB_FIELD);
1075                                         } else {
1076                                             fieldValue.append(SEPERATOR_HYPHEN);
1077                                             isHyphenCodedOnce = true;
1078                                         }
1079                                     }
1080                                 }
1081                                 fieldValue.append(subField.getValue());
1082                             }
1083                         }
1084                     }
1085                     if ((dataField.getTag().equalsIgnoreCase("650") || dataField.getTag().equalsIgnoreCase("651"))
1086                             && fieldValue != null && fieldValue.length() > 1 && fieldValue.toString().trim().length() > 1) {
1087                         String fieldVal = fieldValue.toString().trim();
1088                         String lastChar = String.valueOf(fieldVal.charAt(fieldVal.length() - 1));
1089                         if (!lastChar.equalsIgnoreCase(".")) {
1090                             fieldValue.append(".");
1091                         }
1092                     }
1093                     fieldValues.add(fieldValue.toString().trim());
1094                 }
1095             }
1096         }
1097         if (fieldValues.size() == 1) {
1098             return fieldValues.get(0);
1099         } else if (fieldValues.size() > 0) {
1100             return fieldValues;
1101         } else {
1102             return null;
1103         }
1104     }
1105 
1106     /**
1107      * Method to validate tag with given allowed tag format supplied.
1108      *
1109      * @param tag
1110      * @param tagFormat
1111      * @return
1112      */
1113     private boolean isValidTag(String tag, String tagFormat) {
1114         try {
1115             if (!tagFormat.contains(PATTERN_CHAR)) {
1116                 return tagFormat.equals(tag);
1117             } else {
1118                 int idx = tagFormat.lastIndexOf(PATTERN_CHAR);
1119                 return isValidTag(tag.substring(0, idx) + tag.substring(idx + PATTERN_CHAR.length(), tag.length()), tagFormat.substring(0, idx)
1120                         + tagFormat.substring(idx + PATTERN_CHAR.length(), tagFormat.length()));
1121             }
1122         } catch (Exception e) {
1123             LOG.info("Exception :", e);
1124             return false;
1125         }
1126     }
1127 
1128     private void addGeneralFieldsToSolrDoc(BibMarcRecord record, SolrInputDocument solrDoc) {
1129         String isbnDataFields = documentSearchConfig.FIELDS_TO_TAGS_2_INCLUDE_MAP.get(ISBN_SEARCH);
1130         for (DataField dataField : record.getDataFields()) {
1131             String tag = dataField.getTag();
1132             for (SubField subField : dataField.getSubFields()) {
1133                 String subFieldKey = subField.getCode();
1134                 String subFieldValue = subField.getValue();
1135                 String key = tag + subFieldKey;
1136                 subFieldValue = processGeneralFieldValue(tag, subFieldKey, subFieldValue, isbnDataFields);
1137                 solrDoc.addField(DYNAMIC_FIELD_PREFIX + key, subFieldValue);
1138             }
1139         }
1140     }
1141 
1142     private String processGeneralFieldValue(String tag, String subFieldKey, String subFieldValue, String isbnKey) {
1143         String value = subFieldValue;
1144         if (isbnKey.contains(tag) && isbnKey.contains(subFieldKey)) {
1145             value = (String) normalizeIsbn(subFieldValue);
1146         }
1147         return value;
1148     }
1149 
1150     private Object normalizeIsbn(Object isbnValue) {
1151         Object result = null;
1152         ISBNUtil isbnUtil = new ISBNUtil();
1153         if (isbnValue != null) {
1154             if (isbnValue instanceof List) {
1155                 result = new ArrayList<String>();
1156                 for (Object obj : (List<Object>) isbnValue) {
1157                     if (((String) obj).length() > 0) {
1158                         try {
1159                             ((List<String>) result).add(isbnUtil.normalizeISBN(obj));
1160                         } catch (OleException e) {
1161                             // LOG.error("Exception :", e);
1162                             ((List<String>) result).add((String) obj + " " + ISBN_NOT_NORMALIZED);
1163                         }
1164                     } else {
1165                         ((List<String>) result).add((String) obj);
1166                     }
1167                 }
1168             } else {
1169                 if (((String) isbnValue).length() > 0) {
1170                     try {
1171                         result = isbnUtil.normalizeISBN(isbnValue);
1172                     } catch (OleException e) {
1173                         //  LOG.error("Exception :", e);
1174                         result = isbnValue + " " + ISBN_NOT_NORMALIZED;
1175                     }
1176                 } else {
1177                     result = isbnValue;
1178                 }
1179             }
1180         }
1181         return result;
1182     }
1183 
1184 
1185     /**
1186      * @param pubCen
1187      * @param pubList
1188      */
1189     private void pubCentury(int pubCen, List<String> pubList) {
1190         String pubCentury = String.valueOf(pubCen);
1191         if (pubCentury.endsWith("1")) {
1192             if (pubCentury.equalsIgnoreCase("11")) {
1193                 pubList.add(pubCentury + "th Century");
1194             } else {
1195                 pubList.add(pubCentury + "st Century");
1196             }
1197         } else if (pubCentury.endsWith("2")) {
1198             if (pubCentury.equalsIgnoreCase("12")) {
1199                 pubList.add(pubCentury + "th Century");
1200             } else {
1201                 pubList.add(pubCentury + "nd Century");
1202             }
1203         } else if (pubCentury.endsWith("3")) {
1204             if (pubCentury.equalsIgnoreCase("13")) {
1205                 pubList.add(pubCentury + "th Century");
1206             } else {
1207                 pubList.add(pubCentury + "rd Century");
1208             }
1209         } else {
1210             pubList.add(pubCentury + "th Century");
1211         }
1212 
1213     }
1214 
1215 
1216     public String extractPublicationDateWithRegex(String publicationDate) {
1217         Pattern pattern = Pattern.compile(publicationDateRegex);
1218         Matcher matcher = pattern.matcher(publicationDate);
1219         if (matcher.find()) {
1220             if (matcher.group(0).equalsIgnoreCase("0000")) {
1221                 return "";
1222             }
1223             return matcher.group(0);
1224         } else {
1225             return "";
1226         }
1227 
1228 
1229     }
1230 
1231     /**
1232      * @param publicationDate
1233      * @param publicationEndDate
1234      * @return
1235      */
1236     public Object buildPublicationDateFacetValue(String publicationDate, String publicationEndDate) {
1237         int pubDat = 0;
1238         List<String> pubList = new ArrayList<String>();
1239         Calendar cal = Calendar.getInstance();
1240         int year = cal.get(Calendar.YEAR);
1241         if (publicationDate != null && publicationDate.length() == 4 && Integer.parseInt(publicationDate) <= year) {
1242             int pubStartDate = Integer.parseInt(publicationDate);
1243             if (publicationEndDate != null && publicationEndDate.length() == 4 && pubStartDate < Integer
1244                     .parseInt(publicationEndDate)) {
1245                 if (Integer.parseInt(publicationEndDate) > year) {
1246                     publicationEndDate = String.valueOf(year);
1247                 }
1248                 int pubEndDate = Integer.parseInt(publicationEndDate);
1249                 while (pubStartDate < pubEndDate) {
1250                     pubStartDate = (pubStartDate / 10) * 10;
1251                     if (pubStartDate == 0) {
1252                         pubList.add("Date could not be determined");
1253                     } else {
1254                         pubList.add(String.valueOf(pubStartDate) + "s");
1255                     }
1256                     pubStartDate = pubStartDate + 10;
1257                 }
1258                 pubStartDate = Integer.parseInt(publicationDate);
1259                 pubEndDate = Integer.parseInt(publicationEndDate);
1260                 while (pubStartDate < pubEndDate) {
1261                     pubStartDate = (pubStartDate) / 100;
1262                     pubDat = (pubStartDate) + 1;
1263                     pubCentury(pubDat, pubList);
1264                     pubStartDate = pubStartDate * 100 + 100;
1265                 }
1266             } else {
1267                 pubDat = (pubStartDate / 10) * 10;
1268                 int pubCen = ((pubStartDate) / 100) + 1;
1269                 if (pubDat == 0) {
1270                     pubList.add("Date could not be determined");
1271                 } else {
1272                     pubList.add(String.valueOf(pubDat) + "s");
1273                     pubCentury(pubCen, pubList);
1274                 }
1275             }
1276         } else {
1277             pubList.add("Date could not be determined");
1278         }
1279         return pubList;
1280     }
1281 
1282 
1283     /**
1284      * Builds the facet values for the given publication dates.
1285      *
1286      * @param publicationDates
1287      * @return
1288      */
1289     public List<String> buildPublicationDateFacetValues(List<String> publicationDates) {
1290         List<String> valueList = null;
1291         if (!CollectionUtils.isEmpty(publicationDates)) {
1292             valueList = new ArrayList<String>(publicationDates.size());
1293             for (int i = 0; i < publicationDates.size(); i++) {
1294                 String pubDate = publicationDates.get(i);
1295                 Object pubDt = buildPublicationDateFacetValue(pubDate, "");
1296                 if (pubDt instanceof String) {
1297                     valueList.add((String) pubDt);
1298                 } else if (pubDt instanceof List) {
1299                     List<String> pubDateList = (List<String>) pubDt;
1300                     for (String pubDtVal : pubDateList) {
1301                         valueList.add(pubDtVal);
1302                     }
1303                 }
1304             }
1305         }
1306         return valueList;
1307     }
1308 
1309 
1310     public String getSortString(String str) {
1311         String ret = "";
1312         StringBuffer sortString = new StringBuffer();
1313         ret = str.toLowerCase();
1314         ret = ret.replaceAll("[\\-\\/]", " ");
1315         ret = ret.replace("&lt;", "");
1316         ret = ret.replace("&gt;", "");
1317         ret = ret.replaceAll("[\\.\\,\\;\\:\\(\\)\\{\\}\\'\\!\\?\\\"\\<\\>\\[\\]]", "");
1318         ret = Normalizer.normalize(ret, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
1319         ret = ret.replaceAll("\\s+", " ");
1320         sortString.append(ret);
1321         sortString.append(" /r/n!@#$");
1322         sortString.append(str);
1323         return sortString.toString();
1324     }
1325 
1326     public List<String> getSortString(List<String> list) {
1327         List<String> sortStringList = new ArrayList<String>();
1328         for (String str : list) {
1329             sortStringList.add(getSortString(str));
1330         }
1331         return sortStringList;
1332     }
1333 
1334     private int getSecondIndicator(BibMarcRecord record, String fieldName) {
1335         int ind2Value = 0;
1336         String fieldTags = documentSearchConfig.FIELDS_TO_TAGS_2_INCLUDE_MAP.get(fieldName);
1337         String[] tagValueList = null;
1338         if (fieldTags != null) {
1339             tagValueList = fieldTags.split(",");
1340             List<DataField> dataFieldList = record.getDataFields();
1341             String ind2 = null;
1342             boolean isVisit = true;
1343             for (DataField dataField : dataFieldList) {
1344                 String tag = dataField.getTag();
1345                 for (String tagValue : tagValueList) {
1346                     StringBuffer sb = null;
1347                     if (fieldName.equalsIgnoreCase(AUTHOR_SORT) || fieldName.equalsIgnoreCase(TITLE_SORT)) {
1348                         sb = getTagValues(dataField, tag, tagValue);
1349                         if (sb != null && sb.toString().length() > 0 && isVisit) {
1350                             ind2 = dataField.getInd2();
1351                             isVisit = false;
1352                         }
1353 
1354                     }
1355                 }
1356             }
1357             try {
1358                 if (ind2 != null)
1359                     ind2Value = Integer.parseInt(ind2);
1360 
1361             } catch (Exception e) {
1362                 ind2Value = -1;
1363             }
1364 
1365         }
1366         return ind2Value;
1367     }
1368 
1369     private StringBuffer getTagValues(DataField dataField, String tag, String tagValue) {
1370         StringBuffer sb = new StringBuffer();
1371         String[] tags = tagValue.split("-");
1372         for (String tagName : tags) {
1373             if (tag.equalsIgnoreCase(tagName)) {
1374                 List<SubField> subFieldList = dataField.getSubFields();
1375                 for (SubField subField : subFieldList) {
1376                     sb.append(subField.getValue() + " ");
1377                 }
1378 
1379             }
1380         }
1381 
1382         /*       if (tag.equals(tags[0])) {
1383             LOG.info("tags-->"+tags[0]);
1384             LOG.info("length-->"+tags[0].length());
1385             List<SubField> subFieldList = dataField.getSubFields();
1386             for (SubField subField : subFieldList) {
1387                 sb.append(subField.getValue() + " ");
1388             }
1389         }*/
1390         return sb;
1391     }
1392 
1393     public void bind(String holdingsId, List<String> bibIds) throws SolrServerException, IOException {
1394         List<SolrInputDocument> solrInputDocumentList = new ArrayList<SolrInputDocument>();
1395         updateHoldingsDocument(holdingsId, bibIds, solrInputDocumentList);
1396         updateBibDocument(holdingsId, bibIds, solrInputDocumentList);
1397         LOG.info("solrInputDocumentList-->" + solrInputDocumentList);
1398         SolrServer server = SolrServerManager.getInstance().getSolrServer();
1399         UpdateResponse updateResponse = server.add(solrInputDocumentList);
1400         server.commit();
1401     }
1402 
1403     private void updateBibDocument(String holdingsId, List<String> bibIds, List<SolrInputDocument> solrInputDocumentList) {
1404         for (String bibId : bibIds) {
1405             SolrInputDocument bibSolrInputDocument = new SolrInputDocument();
1406             bibSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, bibId);
1407             Map<String, String> holdingsIdsMap = new HashMap<>();
1408             holdingsIdsMap.put(AtomicUpdateConstants.ADD, holdingsId);
1409             bibSolrInputDocument.setField(HOLDINGS_IDENTIFIER, holdingsIdsMap);
1410             solrInputDocumentList.add(bibSolrInputDocument);
1411         }
1412     }
1413 
1414     private void updateHoldingsDocument(String holdingsId, List<String> bibIds, List<SolrInputDocument> solrInputDocumentList) throws SolrServerException {
1415         SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
1416         holdingsSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, holdingsId);
1417         Map<String, List<String>> bibIdsMap = new HashMap<String, List<String>>();
1418         bibIdsMap.put(AtomicUpdateConstants.ADD, bibIds);
1419         holdingsSolrInputDocument.setField(BIB_ID, bibIdsMap);
1420         holdingsSolrInputDocument.setField(IS_BOUND_WITH, Boolean.TRUE);
1421         solrInputDocumentList.add(holdingsSolrInputDocument);
1422         updateItemDocsOfHoldings(holdingsId, solrInputDocumentList, bibIdsMap);
1423     }
1424 
1425     private void updateItemDocsOfHoldings(String holdingsId, List<SolrInputDocument> solrInputDocumentList, Map<String, List<String>> bibIdsMap) {
1426         SolrDocument holdingsSolrDocument = getSolrDocumentByUUID(holdingsId);
1427         List<String> itemIdentifierList = null;
1428         Object itemIdentifier = holdingsSolrDocument.getFieldValue(ITEM_IDENTIFIER);
1429         if (itemIdentifier instanceof List) {
1430             itemIdentifierList = (List<String>) itemIdentifier;
1431         } else if (itemIdentifier instanceof String) {
1432             itemIdentifierList = new ArrayList<String>();
1433             itemIdentifierList.add((String) itemIdentifier);
1434         }
1435         for (String itemId : itemIdentifierList) {
1436             SolrInputDocument itemSolrInputDocument = new SolrInputDocument();
1437             itemSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, itemId);
1438             itemSolrInputDocument.setField(BIB_ID, bibIdsMap);
1439             solrInputDocumentList.add(itemSolrInputDocument);
1440         }
1441     }
1442 
1443     public void bindAnalytics(String seriesHoldingsId, List<String> itemIds, String createOrBreak) throws SolrServerException, IOException {
1444         List<SolrInputDocument> solrInputDocumentList = new ArrayList<SolrInputDocument>();
1445         updateHoldingsDocument(seriesHoldingsId, itemIds, solrInputDocumentList, createOrBreak);
1446         updateItemDocument(seriesHoldingsId, itemIds, solrInputDocumentList, createOrBreak);
1447         LOG.info("solrInputDocumentList-->" + solrInputDocumentList);
1448         SolrServer server = SolrServerManager.getInstance().getSolrServer();
1449         UpdateResponse updateResponse = server.add(solrInputDocumentList);
1450         server.commit();
1451     }
1452 
1453     private void updateHoldingsDocument(String seriesHoldingsId, List<String> itemIds, List<SolrInputDocument> holdingsSolrInputDocumentList, String createOrBreak) {
1454         SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
1455         holdingsSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, seriesHoldingsId);
1456         Map<String, List<String>> itemIdsMap = new HashMap<String, List<String>>();
1457         if (createOrBreak.equalsIgnoreCase(CREATE_RELATION)) {
1458             itemIdsMap.put(AtomicUpdateConstants.ADD, itemIds);
1459             holdingsSolrInputDocument.setField(ITEM_IDENTIFIER, itemIdsMap);
1460             holdingsSolrInputDocument.setField(IS_SERIES, Boolean.TRUE);
1461             holdingsSolrInputDocument.setField(IS_ANALYTIC, Boolean.TRUE);
1462         } else if (createOrBreak.equalsIgnoreCase(BREAK_RELATION)) {
1463             itemIdsMap.put(AtomicUpdateConstants.REMOVE, itemIds);
1464             holdingsSolrInputDocument.addField(ITEM_IDENTIFIER, itemIdsMap);
1465             if (!hasAnalyticItemInHoldings(seriesHoldingsId, itemIds)) {
1466                 Map analyticMap = new HashMap();
1467                 analyticMap.put(AtomicUpdateConstants.SET, null);
1468                 holdingsSolrInputDocument.setField(IS_SERIES, Boolean.FALSE);
1469                 holdingsSolrInputDocument.setField(IS_ANALYTIC, analyticMap);
1470             }
1471         }
1472         holdingsSolrInputDocumentList.add(holdingsSolrInputDocument);
1473     }
1474 
1475     private void updateItemDocument(String seriesHoldingsId, List<String> itemIds, List<SolrInputDocument> solrInputDocumentList, String createOrBreak) {
1476         for (String itemId : itemIds) {
1477             SolrInputDocument itemSolrInputDocument = new SolrInputDocument();
1478             itemSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, itemId);
1479             Map holdingsIdsMap = new HashMap();
1480             if (createOrBreak.equalsIgnoreCase(CREATE_RELATION)) {
1481                 holdingsIdsMap.put(AtomicUpdateConstants.ADD, seriesHoldingsId);
1482                 itemSolrInputDocument.setField(HOLDINGS_IDENTIFIER, holdingsIdsMap);
1483                 itemSolrInputDocument.setField(IS_ANALYTIC, Boolean.TRUE);
1484                 updateHoldingsDocsOfAnalyticItem(seriesHoldingsId, itemId, itemIds, solrInputDocumentList, CREATE_RELATION);
1485             } else if (createOrBreak.equalsIgnoreCase(BREAK_RELATION)) {
1486                 holdingsIdsMap.put(AtomicUpdateConstants.REMOVE, seriesHoldingsId);
1487                 itemSolrInputDocument.addField(HOLDINGS_IDENTIFIER, holdingsIdsMap);
1488                 itemSolrInputDocument.setField(IS_ANALYTIC, Boolean.FALSE);
1489                 updateHoldingsDocsOfAnalyticItem(seriesHoldingsId, itemId, itemIds, solrInputDocumentList, BREAK_RELATION);
1490             }
1491             solrInputDocumentList.add(itemSolrInputDocument);
1492         }
1493     }
1494 
1495     private void updateHoldingsDocsOfAnalyticItem(String seriesHoldingsId, String itemId, List<String> itemIds, List<SolrInputDocument> solrInputDocumentList, String createOrBreak) {
1496         List<String> holdingsIdentifierList = null;
1497         SolrDocument itemSolrDocument = getSolrDocumentByUUID(itemId);
1498         Object holdingsIdentifier = itemSolrDocument.getFieldValue(HOLDINGS_IDENTIFIER);
1499         if (holdingsIdentifier instanceof List) {
1500             holdingsIdentifierList = (List<String>) holdingsIdentifier;
1501         } else if (holdingsIdentifier instanceof String) {
1502             holdingsIdentifierList = new ArrayList<String>();
1503             holdingsIdentifierList.add((String) holdingsIdentifier);
1504         }
1505         Map analyticMap = new HashMap();
1506         if (CollectionUtils.isNotEmpty(holdingsIdentifierList)) {
1507             for (String holdingsId : holdingsIdentifierList) {
1508                 if (!holdingsId.equalsIgnoreCase(seriesHoldingsId)) {
1509                     if (createOrBreak.equalsIgnoreCase(CREATE_RELATION)) {
1510                         analyticMap.put(AtomicUpdateConstants.SET, Boolean.TRUE);
1511                         SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
1512                         holdingsSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, holdingsId);
1513                         holdingsSolrInputDocument.addField(IS_ANALYTIC, analyticMap);
1514                         solrInputDocumentList.add(holdingsSolrInputDocument);
1515                     } else if (createOrBreak.equalsIgnoreCase(BREAK_RELATION)) {
1516                         if (!hasAnalyticItemInHoldings(holdingsId, itemIds)) {
1517                             analyticMap.put(AtomicUpdateConstants.SET, null);
1518                             SolrInputDocument holdingsSolrInputDocument = new SolrInputDocument();
1519                             holdingsSolrInputDocument.addField(AtomicUpdateConstants.UNIQUE_ID, holdingsId);
1520                             holdingsSolrInputDocument.addField(IS_ANALYTIC, analyticMap);
1521                             solrInputDocumentList.add(holdingsSolrInputDocument);
1522                         }
1523                     }
1524                 }
1525             }
1526         }
1527     }
1528 
1529     private Boolean hasAnalyticItemInHoldings(String holdingsId, List<String> itemIds) {
1530         SolrDocument holdingsSolrDocument = getSolrDocumentByUUID(holdingsId);
1531         List<String> itemIdentifierList = null;
1532         Object itemIdentifier = holdingsSolrDocument.getFieldValue(ITEM_IDENTIFIER);
1533         if (itemIdentifier instanceof List) {
1534             itemIdentifierList = (List<String>) itemIdentifier;
1535         } else if (itemIdentifier instanceof String) {
1536             itemIdentifierList = new ArrayList<String>();
1537             itemIdentifierList.add((String) itemIdentifier);
1538         }
1539         boolean hasAnalytic = false;
1540         for (String itemId : itemIdentifierList) {
1541             if (!itemIds.contains(itemId)) {
1542                 SolrDocument itemSolrDocument = getSolrDocumentByUUID(itemId);
1543                 if (itemSolrDocument.getFieldValue(IS_ANALYTIC) instanceof Boolean) {
1544                     hasAnalytic = (Boolean) itemSolrDocument.getFieldValue(IS_ANALYTIC);
1545                     if (hasAnalytic) {
1546                         break;
1547                     }
1548                 }
1549             }
1550         }
1551         return hasAnalytic;
1552     }
1553 
1554 
1555     public void unbindOne(List<String> holdingsIds, String bibId) throws SolrServerException, IOException {
1556         List<SolrInputDocument> solrInputDocumentList = new ArrayList<>();
1557         updateBibToUnbindOneBib(holdingsIds, bibId, solrInputDocumentList);
1558         SolrServer server = SolrServerManager.getInstance().getSolrServer();
1559         server.add(solrInputDocumentList);
1560         server.commit();
1561     }
1562 
1563     public void unbindAll(List<String> holdingsIds, String bibId) throws SolrServerException, IOException {
1564         List<SolrInputDocument> solrInputDocumentList = new ArrayList<>();
1565         updateBibToUnbindAllBib(holdingsIds, bibId, solrInputDocumentList);
1566         SolrServer server = SolrServerManager.getInstance().getSolrServer();
1567         server.add(solrInputDocumentList);
1568         server.commit();
1569     }
1570 
1571     private void updateBibToUnbindOneBib(List<String> holdingsIds, String bibId, List<SolrInputDocument> solrInputDocumentList) throws SolrServerException {
1572         SolrDocument bibSolrDocument = getSolrDocumentByUUID(bibId);
1573         Object object = bibSolrDocument.getFieldValue(HOLDINGS_IDENTIFIER);
1574         List<String> holdingsIdsSolr = new ArrayList<>();
1575         if (object instanceof List) {
1576             holdingsIdsSolr = (List<String>) object;
1577         } else if (object instanceof String) {
1578             holdingsIdsSolr.add((String) object);
1579         }
1580         for (String holdingsId : holdingsIds) {
1581             holdingsIdsSolr.remove(holdingsId);
1582             SolrDocument holdingsSolrDocument = getSolrDocumentByUUID(holdingsId);
1583             object = holdingsSolrDocument.getFieldValue(BIB_IDENTIFIER);
1584             List<String> bibIds = new ArrayList<>();
1585             if (object instanceof List) {
1586                 bibIds = (List<String>) object;
1587 
1588             } else if (object instanceof String) {
1589                 bibIds.add((String) object);
1590             }
1591             bibIds.remove(bibId);
1592             holdingsSolrDocument.setField(BIB_IDENTIFIER, bibIds);
1593             holdingsSolrDocument.setField("isBoundwith", false);
1594             solrInputDocumentList.add(buildSolrInputDocFromSolrDoc(holdingsSolrDocument));
1595         }
1596         bibSolrDocument.setField(HOLDINGS_IDENTIFIER, holdingsIdsSolr);
1597         solrInputDocumentList.add(buildSolrInputDocFromSolrDoc(bibSolrDocument));
1598     }
1599 
1600     private void updateBibToUnbindAllBib(List<String> holdingsIds, String bibId, List<SolrInputDocument> solrInputDocumentList) throws SolrServerException {
1601         Object object;
1602         Set<String> removedBibIdList = new HashSet<>();
1603         for (String holdingsId : holdingsIds) {
1604             SolrDocument holdingsSolrDocument = getSolrDocumentByUUID(holdingsId);
1605             object = holdingsSolrDocument.getFieldValue(BIB_IDENTIFIER);
1606             List<String> bidIds = new ArrayList<>();
1607             Set<String> bidIdsToAdd = new HashSet<>();
1608             if (object instanceof List) {
1609                 bidIds = (List<String>) object;
1610             } else if (object instanceof String) {
1611                 bidIds.add((String) object);
1612             }
1613             bidIdsToAdd.addAll(bidIds);
1614             for (String bibIdToRemove : bidIds) {
1615                 if (!bibIdToRemove.equals(bibId)) {
1616                     bidIdsToAdd.remove(bibIdToRemove);
1617                     removedBibIdList.add(bibIdToRemove);
1618                 }
1619             }
1620             holdingsSolrDocument.setField(BIB_IDENTIFIER, bidIdsToAdd);
1621             holdingsSolrDocument.setField("isBoundwith", false);
1622             solrInputDocumentList.add(buildSolrInputDocFromSolrDoc(holdingsSolrDocument));
1623         }
1624         for (String removedBibId : removedBibIdList) {
1625             SolrDocument bibsSolrDocument = getSolrDocumentByUUID(removedBibId);
1626             object = bibsSolrDocument.getFieldValue(HOLDINGS_IDENTIFIER);
1627             List<String> holdingsIdList = new ArrayList<>();
1628             Set<String> holdingsIdSet = new HashSet<>();
1629             if (object instanceof List) {
1630                 holdingsIdList = (List<String>) object;
1631             } else if (object instanceof String) {
1632                 holdingsIdList.add((String) object);
1633             }
1634             holdingsIdSet.addAll(holdingsIdList);
1635             for (String holdingsId : holdingsIds) {
1636                 holdingsIdList.remove(holdingsId);
1637             }
1638             bibsSolrDocument.setField(HOLDINGS_IDENTIFIER, holdingsIdList);
1639             solrInputDocumentList.add(buildSolrInputDocFromSolrDoc(bibsSolrDocument));
1640         }
1641     }
1642 
1643 
1644 }