function Document(options) {

    if (!this || this.constructor !== Document) {
        return new Document(options);
    }

    const document = /** @type DocumentDescriptor */options.document || options.doc || options;
    let tmp, keystore;

    this.tree = [];
    this.field = [];
    this.marker = [];
    this.key = (tmp = document.key || document.id) && parse_tree(tmp, this.marker) || "id";

    keystore = options.keystore || 0;
    keystore && (this.keystore = keystore);
    this.fastupdate = !!options.fastupdate;

    /** @type {
     *   Set<string|number>|
     *   Map<Array<string|number>>|
     *   KeystoreSet<string|number>|
     *   KeystoreMap<Array<string|number>>
     * } */
    this.reg = this.fastupdate && !options.worker && !options.db ? keystore && !0 ? new KeystoreMap(keystore) : new Map() : keystore && !0 ? new KeystoreSet(keystore) : new Set();

    this.storetree = (tmp = document.store || null) && tmp && !0 !== tmp && [];
    /** @type {Map|KeystoreMap} */
    this.store = tmp ? keystore && !0 ? new KeystoreMap(keystore) : new Map() : null;

    this.cache = (tmp = options.cache || null) && new Cache(tmp);

    options.cache = !1;

    this.worker = options.worker || !1;

    this.priority = options.priority || 4;

    /**
     * @type {Map<string, Index|WorkerIndex>}
     */
    this.index = parse_descriptor.call(this, options, document);

    this.tag = null;

    if (tmp = document.tag) {
        if ("string" == typeof tmp) {
            tmp = [tmp];
        }
        if (tmp.length) {
            this.tag = new Map();
            this.tagtree = [];
            this.tagfield = [];
            for (let i = 0, params, field; i < tmp.length; i++) {
                params = tmp[i];
                field = params.field || params;
                if (!field) {
                    throw new Error("The tag field from the document descriptor is undefined.");
                }
                if (params.custom) {
                    this.tagtree[i] = params.custom;
                } else {
                    this.tagtree[i] = parse_tree(field, this.marker);
                    if (params.filter) {
                        if ("string" == typeof this.tagtree[i]) {

                            this.tagtree[i] = new String(this.tagtree[i]);
                        }
                        this.tagtree[i]._filter = params.filter;
                    }
                }

                this.tagfield[i] = field;
                this.tag.set(field, new Map());
            }
        }
    }


    if (this.worker) {
        this.fastupdate = !1;
        const promises = [];
        for (const index of this.index.values()) {
            index.then && promises.push(index);
        }
        if (promises.length) {
            const self = this;
            return Promise.all(promises).then(function (result) {
                let count = 0;
                for (const item of self.index.entries()) {
                    const key = /** @type {string} */item[0];
                    let index = /** @type {Index|WorkerIndex | Promise<Index|WorkerIndex>} */item[1];
                    if (index.then) {
                        index = result[count];
                        self.index.set(key, index);
                        count++;
                    }
                }
                return self;
            });
        }
    } else {
        if (options.db) {
            this.fastupdate = !1;

            this.mount(options.db);
        }
    }
}
@constructor
@param {!DocumentOptions} options
@return {Document|Promise<Document>}
@this {Document}