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

io.permazen.core.Index Maven / Gradle / Ivy

Go to download

Permazen core API classes which provide objects, fields, indexes, queries, and schema management on top of a key/value store.

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.core;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import io.permazen.encoding.Encoding;
import io.permazen.kv.KVDatabase;
import io.permazen.kv.KVTransaction;
import io.permazen.util.ByteWriter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * A simple or composite index on some field(s) an {@link ObjType}.
 */
public abstract class Index extends SchemaItem {

    final ObjType objType;
    final List> fields;
    final List> encodings;

// Constructor

    Index(Schema schema, io.permazen.schema.SchemaItem index,
      String name, ObjType objType, Iterable> fields) {
        super(schema, index, name);

        // Initialize
        this.objType = objType;
        this.fields = Collections.unmodifiableList(Lists.newArrayList(fields));
        assert this.fields.size() >= 1 && this.fields.size() <= Database.MAX_INDEXED_FIELDS;

        // Gather encodings, with special handling for reference encodings
        final ArrayList> encodingList = new ArrayList<>(this.fields.size());
        for (int i = 0; i < this.fields.size(); i++)
            encodingList.add(Index.genericize(this.fields.get(i).getEncoding()));
        this.encodings = Collections.unmodifiableList(encodingList);
    }

// Public methods

    /**
     * Get the object type that contains the field(s) in this index.
     *
     * @return indexed fields' object type
     */
    public ObjType getObjType() {
        return this.objType;
    }

    /**
     * Get the indexed field(s).
     *
     * @return list of indexed fields
     */
    public List> getFields() {
        return this.fields;
    }

    /**
     * Get the field encoding(s).
     *
     * 

* Note that the core API treats reference fields with the same name as the same field, regardless of their * object type restrictions. Therefore, the encodings in an index corresponding to reference fields do not * have type restrictions and therefore can be different from the encodings associated with the fields themselves. * * @return list of indexed fields */ public List> getEncodings() { return this.encodings; } /** * Determine whether this is a composite index, i.e., and index on two or more fields. * * @return true if composite */ public boolean isComposite() { return this.fields.size() > 1; } /** * Get this index's view of the given transaction. * * @param tx transaction * @return view of this index in {@code tx} * @throws IllegalArgumentException if {@code tx} is null */ public abstract AbstractCoreIndex getIndex(Transaction tx); /** * Get the {@code byte[]} key in the underlying key/value store corresponding to the given value tuple in this index. * *

* The returned key will be the prefix of all index entries with the given value tuple over all objects. * * @param values indexed values * @return the corresponding {@link KVDatabase} key * @throws IllegalArgumentException if {@code values} is null * @throws IllegalArgumentException if {@code values} has the wrong length for this index * @throws IllegalArgumentException if any value in {@code values} has the wrong type * @see KVTransaction#watchKey KVTransaction.watchKey() */ public byte[] getKey(Object... values) { Preconditions.checkArgument(values != null, "null values"); Preconditions.checkArgument(values.length == this.encodings.size(), "wrong number of values"); final ByteWriter writer = new ByteWriter(); Encodings.UNSIGNED_INT.write(writer, this.storageId); int i = 0; for (Encoding encoding : this.encodings) encoding.validateAndWrite(writer, values[i++]); return writer.getBytes(); } /** * Get the {@code byte[]} key in the underlying key/value store corresponding to the given value tuple and target object * in this index. * * @param id target object ID * @param values indexed values * @return the corresponding {@link KVDatabase} key * @throws IllegalArgumentException if {@code values} is null * @throws IllegalArgumentException if {@code values} has the wrong length for this index * @throws IllegalArgumentException if any value in {@code values} has the wrong type * @see KVTransaction#watchKey KVTransaction.watchKey() */ public byte[] getKey(ObjId id, Object... values) { Preconditions.checkArgument(id != null, "null id"); final ByteWriter writer = new ByteWriter(); writer.write(this.getKey(values)); id.writeTo(writer); return writer.getBytes(); } // IndexSwitch /** * Apply visitor pattern. * * @param target target to invoke * @param visitor return type * @return return value from the method of {@code target} corresponding to this instance's type * @throws NullPointerException if {@code target} is null */ public abstract R visit(IndexSwitch target); // Object @Override public String toString() { return String.format("index \"%s\" on field%s %s", this.name, this.fields.size() != 1 ? "s" : "", this.fields.stream().map(SimpleField::getFullName).collect(Collectors.joining(", "))); } // Package Methods /** * Genericize the given encoding for use in a index. * *

* For encodings other than {@link ReferenceEncoding}, this just returns the given encoding unchanged. * For {@link ReferenceEncoding}, this returns the encoding with all type restrictions removed. * This is required because the type restrictions associated with reference fields are allowed to vary * across schemas without changing the identity of the field. When querying indexes, reference fields * are instead restricted according to the types specified in the query. * * @param encoding simple field encoding * @return {@code encoding} genericized for use in an index, or {@code encoding} itself if no change is needed */ @SuppressWarnings("unchecked") public static Encoding genericize(Encoding encoding) { if (encoding instanceof ReferenceEncoding) encoding = (Encoding)((ReferenceEncoding)encoding).genericizeForIndex(); return encoding; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy