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.codehaus.jackson.JsonGenerationException;
038import org.codehaus.jackson.JsonGenerator;
039import org.nuxeo.ecm.core.api.model.Property;
040import org.nuxeo.ecm.core.api.model.impl.PropertyFactory;
041import org.nuxeo.ecm.core.io.marshallers.json.ExtensibleEntityJsonWriter;
042import org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter;
043import org.nuxeo.ecm.core.io.registry.reflect.Setup;
044import org.nuxeo.ecm.core.schema.SchemaManager;
045import org.nuxeo.ecm.core.schema.types.Field;
046import org.nuxeo.ecm.core.schema.types.ListType;
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
056/**
057 * @since 8.4
058 */
059@SuppressWarnings("rawtypes")
060@Setup(mode = SINGLETON, priority = REFERENCE)
061public class AggregateJsonWriter extends ExtensibleEntityJsonWriter<Aggregate> {
062
063    private static final Log log = LogFactory.getLog(AggregateJsonWriter.class);
064
065    public static final String ENTITY_TYPE = "aggregate";
066
067    public static final String FETCH_KEY = "key";
068
069    @Inject
070    private SchemaManager schemaManager;
071
072    public AggregateJsonWriter() {
073        super(ENTITY_TYPE, Aggregate.class);
074    }
075
076    public AggregateJsonWriter(String entityType, Class<Aggregate> entityClass) {
077        super(entityType, entityClass);
078    }
079
080    @Override
081    public boolean accept(Class<?> clazz, Type genericType, MediaType mediatype) {
082        return true;
083    }
084
085    @SuppressWarnings("unchecked")
086    @Override
087    protected void writeEntityBody(Aggregate agg, JsonGenerator jg) throws IOException {
088        boolean fetch = ctx.getFetched(ENTITY_TYPE).contains(FETCH_KEY);
089        jg.writeObjectField("id", agg.getId());
090        jg.writeObjectField("field", agg.getField());
091        jg.writeObjectField("properties", agg.getProperties());
092        jg.writeObjectField("ranges", agg.getRanges());
093        jg.writeObjectField("selection", agg.getSelection());
094        jg.writeObjectField("type", agg.getType());
095        if (!fetch || !(agg instanceof TermAggregate || agg instanceof SignificantTermAggregate)) {
096            jg.writeObjectField("buckets", agg.getBuckets());
097            jg.writeObjectField("extendedBuckets", agg.getExtendedBuckets());
098        } else {
099            String fieldName = agg.getField();
100            Field field = schemaManager.getField(fieldName);
101            if (field != null) {
102                try (Closeable resource = ctx.wrap()
103                                             .with(FETCH_PROPERTIES + "." + DocumentModelJsonWriter.ENTITY_TYPE,
104                                                     "properties")
105                                             .with(FETCH_PROPERTIES + "." + DirectoryEntryJsonWriter.ENTITY_TYPE,
106                                                     "parent")
107                                             .with(TRANSLATE_PROPERTIES + "." + DirectoryEntryJsonWriter.ENTITY_TYPE,
108                                                     "label")
109                                             .with(MAX_DEPTH_PARAM, "max")
110                                             .open()) {
111
112                    writeBuckets("buckets", agg.getBuckets(), field, jg);
113                    writeBuckets("extendedBuckets", agg.getExtendedBuckets(), field, jg);
114                }
115            } else {
116                log.warn(String.format("Could not resolve field %s for aggrgeate %s", fieldName, agg.getId()));
117                jg.writeObjectField("buckets", agg.getBuckets());
118                jg.writeObjectField("extendedBuckets", agg.getExtendedBuckets());
119            }
120        }
121    }
122
123    protected void writeBuckets(String fieldName, List<Bucket> buckets, Field field, JsonGenerator jg)
124            throws IOException, JsonGenerationException {
125        jg.writeArrayFieldStart(fieldName);
126        for (Bucket bucket : buckets) {
127            jg.writeStartObject();
128
129            jg.writeObjectField("key", bucket.getKey());
130
131            Property prop = PropertyFactory.createProperty(null, field, Property.NONE);
132            if (prop.isList()) {
133                ListType t = (ListType) prop.getType();
134                t.getField();
135                prop = PropertyFactory.createProperty(null, t.getField(), Property.NONE);
136            }
137            log.debug(String.format("Writing %s for field %s resolved to %s", fieldName, field.getName().toString(),
138                    prop.getName()));
139            prop.setValue(bucket.getKey());
140
141            writeEntityField("fetchedKey", prop, jg);
142            jg.writeNumberField("docCount", bucket.getDocCount());
143            jg.writeEndObject();
144
145            if (bucket instanceof BucketRange) {
146                BucketRange bucketRange = (BucketRange) bucket;
147                jg.writeNumberField("from", bucketRange.getFrom());
148                jg.writeNumberField("to", bucketRange.getTo());
149            }
150
151            if (bucket instanceof BucketRangeDate) {
152                BucketRangeDate bucketRange = (BucketRangeDate) bucket;
153                jg.writeStringField("fromAsDate", DateParser.formatW3CDateTime(bucketRange.getFromAsDate().toDate()));
154                jg.writeStringField("toAsDate", DateParser.formatW3CDateTime(bucketRange.getToAsDate().toDate()));
155            }
156        }
157        jg.writeEndArray();
158    }
159
160}