1 package org.collectionspace.services.nuxeo.elasticsearch;
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;
15 import javax.ws.rs.core.HttpHeaders;
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;
30 public class DefaultESDocumentWriter extends JsonESDocumentWriter {
31 private static ObjectMapper objectMapper = new ObjectMapper();
34 public void writeDoc(JsonGenerator jg, DocumentModel doc, String[] schemas,
35 Map<String, String> contextParameters, HttpHeaders headers)
38 ObjectNode denormValues = getDenormValues(doc);
40 jg.writeStartObject();
42 writeSystemProperties(jg, doc);
43 writeSchemas(jg, doc, schemas);
44 writeContextParameters(jg, doc, contextParameters);
45 writeDenormValues(jg, doc, denormValues);
51 public ObjectNode getDenormValues(DocumentModel doc) {
52 ObjectNode denormValues = objectMapper.createObjectNode();
53 String docType = doc.getType();
55 if (docType.startsWith("CollectionObject")) {
56 CoreSession session = doc.getCoreSession();
57 String csid = doc.getName();
58 String tenantId = (String) doc.getProperty("collectionspace_core", "tenantId");
60 denormMediaRecords(session, csid, tenantId, denormValues);
61 denormAcquisitionRecords(session, csid, tenantId, denormValues);
63 // Compute the title of the record for the public browser, and store it so that it can
64 // be used for sorting ES query results.
66 String title = computeTitle(doc);
69 denormValues.put("title", title);
72 // Create a list of production years from the production date structured dates.
74 List<Map<String, Object>> prodDateGroupList = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "objectProductionDateGroupList");
76 denormValues.putArray("prodYears").addAll(structDatesToYearNodes(prodDateGroupList));
82 public void writeDenormValues(JsonGenerator jg, DocumentModel doc, ObjectNode denormValues) throws IOException {
83 if (denormValues != null && denormValues.size() > 0) {
84 if (jg.getCodec() == null) {
85 jg.setCodec(objectMapper);
88 Iterator<Map.Entry<String, JsonNode>> entries = denormValues.getFields();
90 while (entries.hasNext()) {
91 Map.Entry<String, JsonNode> entry = entries.next();
93 jg.writeFieldName("collectionspace_denorm:" + entry.getKey());
94 jg.writeTree(entry.getValue());
99 private void denormMediaRecords(CoreSession session, String csid, String tenantId, ObjectNode denormValues) {
100 // Store the csids of media records that are related to this object.
102 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);
103 DocumentModelList relationDocs = session.query(relatedRecordQuery);
104 List<JsonNode> mediaCsids = new ArrayList<JsonNode>();
106 if (relationDocs.size() > 0) {
107 Iterator<DocumentModel> iterator = relationDocs.iterator();
109 while (iterator.hasNext()) {
110 DocumentModel relationDoc = iterator.next();
111 String mediaCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
113 if (isMediaPublished(session, tenantId, mediaCsid)) {
114 mediaCsids.add(new TextNode(mediaCsid));
119 denormValues.putArray("mediaCsid").addAll(mediaCsids);
120 denormValues.put("hasMedia", mediaCsids.size() > 0);
123 private void denormAcquisitionRecords(CoreSession session, String csid, String tenantId, ObjectNode denormValues) {
124 // Store the credit lines of acquisition records that are related to this object.
126 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);
127 DocumentModelList relationDocs = session.query(relatedRecordQuery);
128 List<JsonNode> creditLines = new ArrayList<JsonNode>();
130 if (relationDocs.size() > 0) {
131 Iterator<DocumentModel> iterator = relationDocs.iterator();
133 while (iterator.hasNext()) {
134 DocumentModel relationDoc = iterator.next();
135 String acquisitionCsid = (String) relationDoc.getProperty("relations_common", "objectCsid");
136 String creditLine = getCreditLine(session, tenantId, acquisitionCsid);
138 if (creditLine != null && creditLine.length() > 0) {
139 creditLines.add(new TextNode(creditLine));
144 denormValues.putArray("creditLine").addAll(creditLines);
148 * Compute a title for the public browser. This needs to be indexed in ES so that it can
149 * be used for sorting. (Even if it's just extracting the primary value.)
151 private String computeTitle(DocumentModel doc) {
152 List<Map<String, Object>> titleGroups = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "titleGroupList");
153 String primaryTitle = null;
155 if (titleGroups.size() > 0) {
156 Map<String, Object> primaryTitleGroup = titleGroups.get(0);
157 primaryTitle = (String) primaryTitleGroup.get("title");
160 if (StringUtils.isNotEmpty(primaryTitle)) {
164 List<Map<String, Object>> objectNameGroups = (List<Map<String, Object>>) doc.getProperty("collectionobjects_common", "objectNameList");
165 String primaryObjectName = null;
167 if (objectNameGroups.size() > 0) {
168 Map<String, Object> primaryObjectNameGroup = objectNameGroups.get(0);
169 primaryObjectName = (String) primaryObjectNameGroup.get("objectName");
172 return primaryObjectName;
175 private boolean isMediaPublished(CoreSession session, String tenantId, String mediaCsid) {
176 boolean isPublished = false;
177 DocumentModel mediaDoc = getRecordByCsid(session, tenantId, "Media", mediaCsid);
179 if (mediaDoc != null) {
180 List<String> publishToValues = (List<String>) mediaDoc.getProperty("media_common", "publishToList");
182 if (publishToValues != null) {
183 for (int i=0; i<publishToValues.size(); i++) {
184 String value = publishToValues.get(i);
185 String shortId = RefNameUtils.getItemShortId(value);
187 if (shortId.equals("all") || shortId.equals("cspacepub")) {
198 private String getCreditLine(CoreSession session, String tenantId, String acquisitionCsid) {
199 String creditLine = null;
200 DocumentModel acquisitionDoc = getRecordByCsid(session, tenantId, "Acquisition", acquisitionCsid);
202 if (acquisitionDoc != null) {
203 creditLine = (String) acquisitionDoc.getProperty("acquisitions_common", "creditLine");
209 protected DocumentModel getRecordByCsid(CoreSession session, String tenantId, String recordType, String csid) {
210 String getRecordQuery = String.format("SELECT * FROM %s WHERE ecm:name = '%s' AND ecm:currentLifeCycleState = 'project' AND collectionspace_core:tenantId = '%s'", recordType, csid, tenantId);
212 DocumentModelList docs = session.query(getRecordQuery);
214 if (docs != null && docs.size() > 0) {
221 protected List<JsonNode> structDateToYearNodes(Map<String, Object> structDate) {
222 return structDatesToYearNodes(Arrays.asList(structDate));
225 protected List<JsonNode> structDatesToYearNodes(List<Map<String, Object>> structDates) {
226 Set<Integer> years = new HashSet<Integer>();
228 for (Map<String, Object> structDate : structDates) {
229 if (structDate != null) {
230 GregorianCalendar earliestCalendar = (GregorianCalendar) structDate.get("dateEarliestScalarValue");
231 GregorianCalendar latestCalendar = (GregorianCalendar) structDate.get("dateLatestScalarValue");
233 if (earliestCalendar != null && latestCalendar != null) {
234 // Grr @ latest scalar value historically being exclusive.
235 // Subtract one day to make it inclusive.
236 latestCalendar.add(Calendar.DATE, -1);
238 Integer earliestYear = earliestCalendar.get(Calendar.YEAR);
239 Integer latestYear = latestCalendar.get(Calendar.YEAR);;
241 for (int year = earliestYear; year <= latestYear; year++) {
248 List<Integer> yearList = new ArrayList<Integer>(years);
249 Collections.sort(yearList);
251 List<JsonNode> yearNodes = new ArrayList<JsonNode>();
253 for (Integer year : yearList) {
254 yearNodes.add(new IntNode(year));