001/* 002 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Guillaume Renard <grenard@nuxeo.com> 018 */ 019package org.nuxeo.elasticsearch.io.marshallers.json; 020 021import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.FETCH_PROPERTIES; 022import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.MAX_DEPTH_PARAM; 023import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.TRANSLATE_PROPERTIES; 024import static org.nuxeo.ecm.core.io.registry.reflect.Instantiations.SINGLETON; 025import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE; 026 027import java.io.Closeable; 028import java.io.IOException; 029import java.lang.reflect.Type; 030import java.util.List; 031 032import javax.inject.Inject; 033import javax.ws.rs.core.MediaType; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.nuxeo.ecm.core.api.model.Property; 038import org.nuxeo.ecm.core.api.model.impl.DocumentPartImpl; 039import org.nuxeo.ecm.core.api.model.impl.PropertyFactory; 040import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter; 041import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter; 042import org.nuxeo.ecm.core.io.registry.reflect.Setup; 043import org.nuxeo.ecm.core.schema.SchemaManager; 044import org.nuxeo.ecm.core.schema.types.Field; 045import org.nuxeo.ecm.core.schema.types.ListType; 046import org.nuxeo.ecm.core.schema.types.Schema; 047import org.nuxeo.ecm.core.schema.utils.DateParser; 048import org.nuxeo.ecm.directory.io.DirectoryEntryJsonWriter; 049import org.nuxeo.ecm.platform.query.api.Aggregate; 050import org.nuxeo.ecm.platform.query.api.Bucket; 051import org.nuxeo.ecm.platform.query.core.BucketRange; 052import org.nuxeo.ecm.platform.query.core.BucketRangeDate; 053import org.nuxeo.elasticsearch.aggregate.SignificantTermAggregate; 054import org.nuxeo.elasticsearch.aggregate.TermAggregate; 055 056import com.fasterxml.jackson.core.JsonGenerationException; 057import com.fasterxml.jackson.core.JsonGenerator; 058 059/** 060 * @since 8.4 061 */ 062@SuppressWarnings("rawtypes") 063@Setup(mode = SINGLETON, priority = REFERENCE) 064public class AggregateJsonWriter extends ExtensibleEntityJsonWriter<Aggregate> { 065 066 public static final String ENTITY_TYPE = "aggregate"; 067 068 public static final String FETCH_KEY = "key"; 069 070 private static final Log log = LogFactory.getLog(AggregateJsonWriter.class); 071 072 @Inject 073 private SchemaManager schemaManager; 074 075 public AggregateJsonWriter() { 076 super(ENTITY_TYPE, Aggregate.class); 077 } 078 079 public AggregateJsonWriter(String entityType, Class<Aggregate> entityClass) { 080 super(entityType, entityClass); 081 } 082 083 @Override 084 public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) { 085 return true; 086 } 087 088 @SuppressWarnings("unchecked") 089 @Override 090 protected void writeEntityBody(Aggregate agg, JsonGenerator jg) throws IOException { 091 boolean fetch = ctx.getFetched(ENTITY_TYPE).contains(FETCH_KEY); 092 jg.writeObjectField("id", agg.getId()); 093 jg.writeObjectField("field", agg.getField()); 094 jg.writeObjectField("properties", agg.getProperties()); 095 jg.writeObjectField("ranges", agg.getRanges()); 096 jg.writeObjectField("selection", agg.getSelection()); 097 jg.writeObjectField("type", agg.getType()); 098 if (!fetch || !(agg instanceof TermAggregate || agg instanceof SignificantTermAggregate)) { 099 jg.writeObjectField("buckets", agg.getBuckets()); 100 jg.writeObjectField("extendedBuckets", agg.getExtendedBuckets()); 101 } else { 102 String fieldName = agg.getField(); 103 Field field = schemaManager.getField(fieldName); 104 if (field != null) { 105 try (Closeable resource = ctx.wrap() 106 .with(FETCH_PROPERTIES + "." + DocumentModelJsonWriter.ENTITY_TYPE, 107 "properties") 108 .with(FETCH_PROPERTIES + "." + DirectoryEntryJsonWriter.ENTITY_TYPE, 109 "parent") 110 .with(TRANSLATE_PROPERTIES + "." + DirectoryEntryJsonWriter.ENTITY_TYPE, 111 "label") 112 .with(MAX_DEPTH_PARAM, "max") 113 .open()) { 114 115 writeBuckets("buckets", agg.getBuckets(), field, jg); 116 writeBuckets("extendedBuckets", agg.getExtendedBuckets(), field, jg); 117 } 118 } else { 119 log.warn(String.format("Could not resolve field %s for aggregate %s", fieldName, agg.getId())); 120 jg.writeObjectField("buckets", agg.getBuckets()); 121 jg.writeObjectField("extendedBuckets", agg.getExtendedBuckets()); 122 } 123 } 124 } 125 126 protected void writeBuckets(String fieldName, List<Bucket> buckets, Field field, JsonGenerator jg) 127 throws IOException, JsonGenerationException { 128 // prepare document part in order to use property 129 Schema schema = field.getDeclaringType().getSchema(); 130 DocumentPartImpl part = new DocumentPartImpl(schema); 131 // write data 132 jg.writeArrayFieldStart(fieldName); 133 for (Bucket bucket : buckets) { 134 jg.writeStartObject(); 135 136 jg.writeObjectField("key", bucket.getKey()); 137 138 Property prop = PropertyFactory.createProperty(part, field, Property.NONE); 139 if (prop.isList()) { 140 ListType t = (ListType) prop.getType(); 141 t.getField(); 142 prop = PropertyFactory.createProperty(part, t.getField(), Property.NONE); 143 } 144 log.debug(String.format("Writing %s for field %s resolved to %s", fieldName, field.getName().toString(), 145 prop.getName())); 146 prop.setValue(bucket.getKey()); 147 148 writeEntityField("fetchedKey", prop, jg); 149 jg.writeNumberField("docCount", bucket.getDocCount()); 150 jg.writeEndObject(); 151 152 if (bucket instanceof BucketRange) { 153 BucketRange bucketRange = (BucketRange) bucket; 154 jg.writeNumberField("from", bucketRange.getFrom()); 155 jg.writeNumberField("to", bucketRange.getTo()); 156 } 157 158 if (bucket instanceof BucketRangeDate) { 159 BucketRangeDate bucketRange = (BucketRangeDate) bucket; 160 jg.writeStringField("fromAsDate", DateParser.formatW3CDateTime(bucketRange.getFromAsDate().toDate())); 161 jg.writeStringField("toAsDate", DateParser.formatW3CDateTime(bucketRange.getToAsDate().toDate())); 162 } 163 } 164 jg.writeEndArray(); 165 } 166 167}