All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.document.Document Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.document;

import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.Struct;
import com.yahoo.document.datatypes.StructuredFieldValue;
import com.yahoo.document.json.JsonWriter;
import com.yahoo.document.serialization.DocumentReader;
import com.yahoo.document.serialization.DocumentSerializer;
import com.yahoo.document.serialization.DocumentSerializerFactory;
import com.yahoo.document.serialization.DocumentWriter;
import com.yahoo.document.serialization.FieldReader;
import com.yahoo.document.serialization.FieldWriter;
import com.yahoo.document.serialization.SerializationException;
import com.yahoo.document.serialization.XmlSerializationHelper;
import com.yahoo.document.serialization.XmlStream;
import com.yahoo.io.GrowableByteBuffer;
import com.yahoo.vespa.objects.Ids;
import com.yahoo.vespa.objects.Serializer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;

/**
 * A document is an identifiable
 * set of value bindings of a {@link DocumentType document type}.
 * A document represents an instance of some entity of interest
 * in an application, like an article, a web document, a product, etc.
 *
 * Deprecation: Try to use document set and get methods only with FieldValue types,
 * not with primitive types. Support for direct access to primitive types will
 * be removed soon.
 *
 * @author bratseth
 * @author Einar M R Rosenvinge
 */
public class Document extends StructuredFieldValue {

    public static final int classId = registerClass(Ids.document + 3, Document.class);
    public static final short SERIALIZED_VERSION = 8;
    private DocumentId docId;
    private Struct content;
    private Long lastModified = null;

    /**
     * Create a document with the given document type and identifier.
     * @param docType DocumentType to use for creation
     * @param id The id for this document
     */
    public Document(DocumentType docType, String id) {
        this(docType, new DocumentId(id));
    }

    /**
     * Create a document with the given document type and identifier.
     * @param docType DocumentType to use for creation
     * @param id The id for this document
     */
    public Document(DocumentType docType, DocumentId id) {
        super(docType);
        setNewType(docType);
        internalSetId(id, docType);
    }

    /**
     * Creates a document that is a shallow copy of another.
     *
     * @param doc The document to copy.
     */
    public Document(Document doc) {
        this(doc.getDataType(), doc.getId());
        content = doc.content;
        lastModified = doc.lastModified;
    }

    /**
     *
     * @param reader The deserializer to use for creating this document
     */
    public Document(DocumentReader reader) {
        super(null);
        reader.read(this);
    }

    public DocumentId getId() { return docId; }
    public void setId(DocumentId id) { internalSetId(id, getDataType()); }
    private void internalSetId(DocumentId id, DocumentType docType) {
        if (id != null && id.hasDocType() && docType != null && !id.getDocType().equals(docType.getName())) {
            throw new IllegalArgumentException("Trying to set a document id (type " + id.getDocType() +
                                               ") that doesn't match the document type (" + getDataType().getName() + ").");
        }
        docId = id;
    }

    @Override
    public void assign(Object o) {
        throw new IllegalArgumentException("Assign not implemented for " + getClass() + " objects");
    }

    @Override
    public Document clone() {
        Document doc = (Document) super.clone();
        doc.docId = docId.clone();
        doc.content = content.clone();
        return doc;
    }

    private void setNewType(DocumentType type) {
        content = type.contentStruct().createFieldValue();
    }

    public void setDataType(DataType type) {
        if (docId != null && docId.hasDocType() && !docId.getDocType().equals(type.getName())) {
            throw new IllegalArgumentException("Trying to set a document type (" + type.getName() +
                                               ") that doesn't match the document id (" + docId + ").");
        }
        super.setDataType(type);
        setNewType((DocumentType)type);
    }

    public int getSerializedSize() throws SerializationException {
        DocumentSerializer data = DocumentSerializerFactory.createHead(new GrowableByteBuffer(8 * 1024, 2.0f));
        data.write(this);
        return data.getBuf().position();
    }

    /**
     *  This is an approximation of serialized size. We just set it to 4096 as a definition of a medium document.
     * @return Approximate size of document (4096)
     */
    public final int getApproxSize() { return 4096; }

    public void serialize(OutputStream out) throws SerializationException {
        DocumentSerializer writer = DocumentSerializerFactory.createHead(new GrowableByteBuffer(8 * 1024, 2.0f));
        writer.write(this);
        GrowableByteBuffer data = writer.getBuf();
        byte[] array;
        if (data.hasArray()) {
            //just get the array
            array = data.array();
        } else {
            //copy the bytebuffer into the array
            array = new byte[data.position()];
            int endPos = data.position();
            data.position(0);
            data.get(array);
            data.position(endPos);
        }
        try {
            out.write(array, 0, data.position());
        } catch (IOException ioe) {
            throw new SerializationException(ioe);
        }
    }

    public static Document createDocument(DocumentReader buffer) {
        return new Document(buffer);
    }

    @Override
    public Field getField(String fieldName) {
        Field field = content.getField(fieldName);
        if (field == null) {
            for(DocumentType parent : getDataType().getInheritedTypes()) {
                field = parent.getField(fieldName);
                if (field != null) {
                    break;
                }
            }
        }
        return field;
    }

    @Override
    public FieldValue getFieldValue(Field field) {
        return content.getFieldValue(field);
    }

    @Override
    protected void doSetFieldValue(Field field, FieldValue value) {
        content.setFieldValue(field, value);
    }

    @Override
    public FieldValue removeFieldValue(Field field) {
        return content.removeFieldValue(field);
    }

    @Override
    public void clear() {
        content.clear();
    }

    @Override
    public Iterator> iterator() {
        return content.iterator();
    }

    public String toString() {
        return "document '" + docId + "' of type '" + getDataType().getName() + "'";
    }

    @Deprecated
    public String toXML(String indent) {
        XmlStream xml = new XmlStream();
        xml.setIndent(indent);
        xml.beginTag("document");
        printXml(xml);
        xml.endTag();
        return xml.toString();
    }

    /**
     * Get XML representation of the document root and its children, contained
     * within a <document></document> tag.
     * @return XML representation of document
     */
    @Deprecated
    public String toXml() {
        return toXML("  ");
    }

    @Deprecated
    public void printXml(XmlStream xml) {
        XmlSerializationHelper.printDocumentXml(this, xml);
    }

    /**
     * Get JSON representation of the document root and its children contained in a JSON object
     *
     * @return JSON representation of document
     */
    public String toJson() {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        JsonWriter writer = new JsonWriter(buffer);
        writer.write(this);
        return buffer.toString(StandardCharsets.UTF_8);
    }

    /** Returns true if the argument is a document which has the same set of values */
    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof Document other)) return false;
        return (super.equals(o) && docId.equals(other.docId) &&
                content.equals(other.content));
    }

    @Override
    public int hashCode() {
        return 31 * super.hashCode() + (docId != null ? docId.hashCode() : 0);
    }

    /**
     * Returns the last modified time of this Document, when stored in persistent storage. This is typically set by the
     * library that retrieves the Document from persistent storage.
     *
     * This variable doesn't really belong in document. It is used when retrieving docblocks of documents to be able to
     * see when documents was last modified in VDS, without having to add modified times separate in the API.
     *
     * NOTE: This is a transient field, and will not be serialized with a Document (will be null after deserialization).
     *
     * @return the last modified time of this Document (in milliseconds), or null if unset
     */
    public Long getLastModified() {
        return lastModified;
    }

    /**
     * Sets the last modified time of this Document. This is typically set by the library that retrieves the
     * Document from persistent storage, and should not be set by arbitrary clients. NOTE: This is a
     * transient field, and will not be serialized with a Document (will be null after deserialization).
     *
     * @param lastModified the last modified time of this Document (in milliseconds)
     */
    public void setLastModified(Long lastModified) {
        this.lastModified = lastModified;
    }

    public void onSerialize(Serializer data) throws SerializationException {
        serialize((DocumentWriter)data);
    }

    @Override
    public DocumentType getDataType() {
        return (DocumentType)super.getDataType();
    }

    @Override
    public int getFieldCount() {
        return content.getFieldCount();
    }

    public void serialize(DocumentWriter writer) {
        writer.write(this);
    }

    public void deserialize(DocumentReader reader) {
        reader.read(this);
    }

    @Override
    public void serialize(Field field, FieldWriter writer) {
        writer.write(field, this);
    }

    /* (non-Javadoc)
     * @see com.yahoo.document.datatypes.FieldValue#deserialize(com.yahoo.document.Field, com.yahoo.document.serialization.FieldReader)
     */
    @Override
    public void deserialize(Field field, FieldReader reader) {
        reader.read(field, this);
    }

    @Override
    public int compareTo(FieldValue fieldValue) {
        int comp = super.compareTo(fieldValue);

        if (comp != 0) {
            return comp;
        }

        //types are equal, this must be of this type
        Document otherValue = (Document) fieldValue;
        comp = getId().compareTo(otherValue.getId());

        if (comp != 0) {
            return comp;
        }

        comp = content.compareTo(otherValue.content);

        if (comp != 0) {
            return comp;
        }

        return comp;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy