]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
b5de58555a2d25392c8b8bcc5ef1d14cee5f88fb
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.nuxeo.elasticsearch;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Arrays;
6 import java.util.Calendar;
7 import java.util.Collections;
8 import java.util.GregorianCalendar;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14
15 import javax.ws.rs.core.HttpHeaders;
16
17 import org.apache.commons.lang3.StringUtils;
18 import org.codehaus.jackson.JsonGenerator;
19 import org.codehaus.jackson.JsonNode;
20 import org.codehaus.jackson.map.ObjectMapper;
21 import org.codehaus.jackson.node.IntNode;
22 import org.codehaus.jackson.node.ObjectNode;
23 import org.codehaus.jackson.node.TextNode;
24 import org.collectionspace.services.common.api.RefNameUtils;
25 import org.nuxeo.ecm.automation.jaxrs.io.documents.JsonESDocumentWriter;
26 import org.nuxeo.ecm.core.api.CoreSession;
27 import org.nuxeo.ecm.core.api.DocumentModel;
28 import org.nuxeo.ecm.core.api.DocumentModelList;
29
30 public class DefaultESDocumentWriter extends JsonESDocumentWriter {
31         private static ObjectMapper objectMapper = new ObjectMapper();
32
33         @Override
34         public void writeDoc(JsonGenerator jg, DocumentModel doc, String[] schemas,
35                         Map<String, String> contextParameters, HttpHeaders headers)
36                         throws IOException {
37
38                 ObjectNode denormValues = getDenormValues(doc);
39
40                 jg.writeStartObject();
41
42                 writeSystemProperties(jg, doc);
43                 writeSchemas(jg, doc, schemas);
44                 writeContextParameters(jg, doc, contextParameters);
45                 writeDenormValues(jg, doc, denormValues);
46
47                 jg.writeEndObject();
48                 jg.flush();
49         }
50
51         public ObjectNode getDenormValues(DocumentModel doc) {
52                 ObjectNode denormValues = objectMapper.createObjectNode();
53                 String docType = doc.getType();
54
55                 if (docType.startsWith("CollectionObject")) {
56                         CoreSession session = doc.getCoreSession();
57                         String csid = doc.getName();
58                         String tenantId = (String) doc.getProperty("collectionspace_core", "tenantId");
59
60                         denormMediaRecords(session, csid, tenantId, denormValues);
61                         denormAcquisitionRecords(session, csid, tenantId, denormValues);
62                         denormExhibitionRecords(session, csid, tenantId, denormValues);
63                         denormMaterialFields(doc, denormValues);
64                         denormObjectNameFields(doc, denormValues);
65
66                         // Compute the title of the record for the public browser, and store it so that it can
67                         // be used for sorting ES query results.
68
69                         String title = computeTitle(doc);
70
71                         if (title != null) {
72                                 denormValues.put("title", title);
73                         }
74
75                         // Create a list of production years from the production date structured dates.
76
77                         List<Map<String, Object>> prodDateGroupList = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "objectProductionDateGroupList");
78
79                         denormValues.putArray("prodYears").addAll(structDatesToYearNodes(prodDateGroupList));
80                 }
81
82                 return denormValues;
83         }
84
85         public void writeDenormValues(JsonGenerator jg, DocumentModel doc, ObjectNode denormValues) throws IOException {
86                 if (denormValues != null && denormValues.size() > 0) {
87                         if (jg.getCodec() == null) {
88                                 jg.setCodec(objectMapper);
89                         }
90
91                         Iterator<Map.Entry<String, JsonNode>> entries = denormValues.getFields();
92
93                         while (entries.hasNext()) {
94                                 Map.Entry<String, JsonNode> entry = entries.next();
95
96                                 jg.writeFieldName("collectionspace_denorm:" + entry.getKey());
97                                 jg.writeTree(entry.getValue());
98                         }
99                 }
100         }
101
102         private void denormMediaRecords(CoreSession session, String csid, String tenantId, ObjectNode denormValues) {
103                 // Store the csid and alt text of media records that are related to this object.
104
105                 String relatedRecordQuery = String.format("SELECT * FROM Relation WHERE relations_common:subjectCsid = '%s' AND relations_common:objectDocumentType = 'Media' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", csid, tenantId);
106                 DocumentModelList relationDocs = session.query(relatedRecordQuery);
107                 List<JsonNode> mediaCsids = new ArrayList<JsonNode>();
108                 List<JsonNode> mediaAltTexts = new ArrayList<JsonNode>();
109
110                 if (relationDocs.size() > 0) {
111                         Iterator<DocumentModel> iterator = relationDocs.iterator();
112
113                         while (iterator.hasNext()) {
114                                 DocumentModel relationDoc = iterator.next();
115                                 String mediaCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
116                                 DocumentModel mediaDoc = getRecordByCsid(session, tenantId, "Media", mediaCsid);
117
118                                 if (isMediaPublished(mediaDoc)) {
119                                         mediaCsids.add(new TextNode(mediaCsid));
120
121                                         String altText = (String) mediaDoc.getProperty("media_common", "altText");
122
123                                         if (altText == null) {
124                                                 altText = "";
125                                         }
126
127                                         mediaAltTexts.add(new TextNode(altText));
128                                 }
129                         }
130                 }
131
132                 denormValues.putArray("mediaCsid").addAll(mediaCsids);
133                 denormValues.putArray("mediaAltText").addAll(mediaAltTexts);
134                 denormValues.put("hasMedia", mediaCsids.size() > 0);
135         }
136
137         private void denormAcquisitionRecords(CoreSession session, String csid, String tenantId, ObjectNode denormValues) {
138                 // Store the credit lines of acquisition records that are related to this object.
139
140                 String relatedRecordQuery = String.format("SELECT * FROM Relation WHERE relations_common:subjectCsid = '%s' AND relations_common:objectDocumentType = 'Acquisition' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", csid, tenantId);
141                 DocumentModelList relationDocs = session.query(relatedRecordQuery);
142                 List<JsonNode> creditLines = new ArrayList<JsonNode>();
143
144                 if (relationDocs.size() > 0) {
145                         Iterator<DocumentModel> iterator = relationDocs.iterator();
146
147                         while (iterator.hasNext()) {
148                                 DocumentModel relationDoc = iterator.next();
149                                 String acquisitionCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
150                                 String creditLine = getCreditLine(session, tenantId, acquisitionCsid);
151
152                                 if (creditLine != null && creditLine.length() > 0) {
153                                         creditLines.add(new TextNode(creditLine));
154                                 }
155                         }
156                 }
157
158                 denormValues.putArray("creditLine").addAll(creditLines);
159 }
160
161 private void denormExhibitionRecords(CoreSession session, String csid, String tenantId, ObjectNode denormValues) {
162         // Store the title, general note, and curatorial note of exhibition records that are published, and related to this object.
163
164         String relatedRecordQuery = String.format("SELECT * FROM Relation WHERE relations_common:subjectCsid = '%s' AND relations_common:objectDocumentType = 'Exhibition' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", csid, tenantId);
165         DocumentModelList relationDocs = session.query(relatedRecordQuery);
166         List<JsonNode> exhibitions = new ArrayList<JsonNode>();
167
168         if (relationDocs.size() > 0) {
169                 Iterator<DocumentModel> iterator = relationDocs.iterator();
170
171                 while (iterator.hasNext()) {
172                         DocumentModel relationDoc = iterator.next();
173                         String exhibitionCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
174                         DocumentModel exhibitionDoc = getRecordByCsid(session, tenantId, "Exhibition", exhibitionCsid);
175
176                         if (exhibitionDoc != null && isExhibitionPublished(exhibitionDoc)) {
177                                 ObjectNode exhibitionNode = objectMapper.createObjectNode();
178
179                                 String title = (String) exhibitionDoc.getProperty("exhibitions_common", "title");
180                                 String generalNote = (String) exhibitionDoc.getProperty("exhibitions_common", "generalNote");
181                                 String curatorialNote = (String) exhibitionDoc.getProperty("exhibitions_common", "curatorialNote");
182
183                                 exhibitionNode.put("title", title);
184                                 exhibitionNode.put("generalNote", generalNote);
185                                 exhibitionNode.put("curatorialNote", curatorialNote);
186
187                                 exhibitions.add(exhibitionNode);
188                         }
189                 }
190         }
191
192         denormValues.putArray("exhibition").addAll(exhibitions);
193 }
194
195         /**
196          * Denormalize the material group list for a collectionobject in order to index the controlled or uncontrolled term
197          *
198          * @param doc the collectionobject document
199          * @param denormValues the json node for denormalized fields
200          */
201         private void denormMaterialFields(DocumentModel doc, ObjectNode denormValues) {
202                 List<Map<String, Object>> materialGroupList =
203                         (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "materialGroupList");
204
205                 List<JsonNode> denormMaterials = new ArrayList<>();
206                 for (Map<String, Object> materialGroup : materialGroupList) {
207                         String controlledMaterial = (String) materialGroup.get("materialControlled");
208                         if (controlledMaterial != null) {
209                                 final ObjectNode node = objectMapper.createObjectNode();
210                                 node.put("material", RefNameUtils.getDisplayName(controlledMaterial));
211                                 denormMaterials.add(node);
212                         }
213
214                         String material = (String) materialGroup.get("material");
215                         if (material != null) {
216                                 final ObjectNode node = objectMapper.createObjectNode();
217                                 node.put("material", material);
218                                 denormMaterials.add(node);
219                         }
220                 }
221
222                 denormValues.putArray("materialGroupList").addAll(denormMaterials);
223         }
224
225         /**
226          * Denormalize the object name group list for a collectionobject in order to index the controlled and
227          * uncontrolled terms
228          *
229          * @param doc the collectionobject document
230          * @param denormValues the json node for denormalized fields
231          */
232         private void denormObjectNameFields(DocumentModel doc, ObjectNode denormValues) {
233                 List<Map<String, Object>> objectNameList =
234                         (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "objectNameList");
235
236                 List<JsonNode> denormObjectNames = new ArrayList<>();
237                 for (Map<String, Object> objectNameGroup  : objectNameList) {
238                         String controlledName = (String) objectNameGroup.get("objectNameControlled");
239                         if (controlledName != null) {
240                                 final ObjectNode node = objectMapper.createObjectNode();
241                                 node.put("objectName", RefNameUtils.getDisplayName(controlledName));
242                                 denormObjectNames.add(node);
243                         }
244
245                         String objectName = (String) objectNameGroup.get("objectName");
246                         if (objectName != null) {
247                                 final ObjectNode node = objectMapper.createObjectNode();
248                                 node.put("objectName", objectName);
249                                 denormObjectNames.add(node);
250                         }
251                 }
252
253                 denormValues.putArray("objectNameList").addAll(denormObjectNames);
254         }
255
256         /**
257          * Compute a title for the public browser. This needs to be indexed in ES so that it can
258          * be used for sorting. (Even if it's just extracting the primary value.)
259          */
260         protected String computeTitle(DocumentModel doc) {
261                 List<Map<String, Object>> titleGroups = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "titleGroupList");
262                 String primaryTitle = null;
263
264                 if (titleGroups.size() > 0) {
265                         Map<String, Object> primaryTitleGroup = titleGroups.get(0);
266                         primaryTitle = (String) primaryTitleGroup.get("title");
267                 }
268
269                 if (StringUtils.isNotEmpty(primaryTitle)) {
270                         return primaryTitle;
271                 }
272
273                 List<Map<String, Object>> objectNameGroups = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "objectNameList");
274                 String primaryObjectName = null;
275
276                 if (objectNameGroups.size() > 0) {
277                         Map<String, Object> primaryObjectNameGroup = objectNameGroups.get(0);
278                         primaryObjectName = (String) primaryObjectNameGroup.get("objectNameControlled");
279                         if (primaryObjectName == null) {
280                                 primaryObjectName = (String) primaryObjectNameGroup.get("objectName");
281                         }
282
283                         // The object might be a refname in some profiles/tenants. If it is, use only the display name.
284
285                         try {
286                                 String displayName = RefNameUtils.getDisplayName(primaryObjectName);
287
288                                 if (displayName != null) {
289                                         primaryObjectName = displayName;
290                                 }
291                         }
292                         catch (Exception e) {}
293                 }
294
295                 return primaryObjectName;
296         }
297
298         private boolean isPublished(DocumentModel doc, String publishedFieldPart, String publishedFieldName) {
299                 boolean isPublished = false;
300
301                 if (doc != null) {
302                         List<String> publishToValues = (List<String>) doc.getProperty(publishedFieldPart, publishedFieldName);
303
304                         if (publishToValues != null) {
305                                 for (int i=0; i<publishToValues.size(); i++) {
306                                         String value = publishToValues.get(i);
307                                         String shortId = RefNameUtils.getItemShortId(value);
308
309                                         if (shortId.equals("all") || shortId.equals("cspacepub")) {
310                                                 isPublished = true;
311                                                 break;
312                                         }
313                                 }
314                         }
315                 }
316
317                 return isPublished;
318
319         }
320
321         private boolean isMediaPublished(DocumentModel mediaDoc) {
322                 return isPublished(mediaDoc, "media_common", "publishToList");
323         }
324
325         private boolean isExhibitionPublished(DocumentModel exhibitionDoc) {
326                 return isPublished(exhibitionDoc, "exhibitions_common", "publishToList");
327         }
328
329         private String getCreditLine(CoreSession session, String tenantId, String acquisitionCsid) {
330                 String creditLine = null;
331                 DocumentModel acquisitionDoc = getRecordByCsid(session, tenantId, "Acquisition", acquisitionCsid);
332
333                 if (acquisitionDoc != null) {
334                         creditLine = (String) acquisitionDoc.getProperty("acquisitions_common", "creditLine");
335                 }
336
337                 return creditLine;
338         }
339
340         protected DocumentModel getRecordByCsid(CoreSession session, String tenantId, String recordType, String csid) {
341                 String getRecordQuery = String.format("SELECT * FROM %s WHERE ecm:name = '%s' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", recordType, csid, tenantId);
342
343                 DocumentModelList docs = session.query(getRecordQuery);
344
345                 if (docs != null && docs.size() > 0) {
346                         return docs.get(0);
347                 }
348
349                 return null;
350         }
351
352         protected List<JsonNode> structDateToYearNodes(Map<String, Object> structDate) {
353                 return structDatesToYearNodes(Arrays.asList(structDate));
354         }
355
356         protected List<JsonNode> structDatesToYearNodes(List<Map<String, Object>> structDates) {
357                 Set<Integer> years = new HashSet<Integer>();
358
359                 for (Map<String, Object> structDate : structDates) {
360                         if (structDate != null) {
361                                 GregorianCalendar earliestCalendar = (GregorianCalendar) structDate.get("dateEarliestScalarValue");
362                                 GregorianCalendar latestCalendar = (GregorianCalendar) structDate.get("dateLatestScalarValue");
363
364                                 if (earliestCalendar != null && latestCalendar != null) {
365                                         // Grr @ latest scalar value historically being exclusive.
366                                         // Subtract one day to make it inclusive.
367                                         latestCalendar.add(Calendar.DATE, -1);
368
369                                         Integer earliestYear = earliestCalendar.get(Calendar.YEAR);
370                                         Integer latestYear = latestCalendar.get(Calendar.YEAR);;
371
372                                         for (int year = earliestYear; year <= latestYear; year++) {
373                                                 years.add(year);
374                                         }
375                                 }
376                         }
377                 }
378
379                 List<Integer> yearList = new ArrayList<Integer>(years);
380                 Collections.sort(yearList);
381
382                 List<JsonNode> yearNodes = new ArrayList<JsonNode>();
383
384                 for (Integer year : yearList) {
385                         yearNodes.add(new IntNode(year));
386                 }
387
388                 return yearNodes;
389         }
390 }