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

jetbrains.exodus.entitystore.iterate.PropertiesIterable Maven / Gradle / Ivy

/**
 * Copyright 2010 - 2020 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.exodus.entitystore.iterate;

import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.bindings.ComparableBinding;
import jetbrains.exodus.bindings.ComparableSet;
import jetbrains.exodus.bindings.ComparableValueType;
import jetbrains.exodus.bindings.LongBinding;
import jetbrains.exodus.entitystore.*;
import jetbrains.exodus.entitystore.tables.PropertyKey;
import jetbrains.exodus.entitystore.tables.PropertyValue;
import jetbrains.exodus.env.Cursor;
import jetbrains.exodus.env.Store;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@SuppressWarnings({"RawUseOfParameterizedType"})
public class PropertiesIterable extends EntityIterableBase {

    private final int entityTypeId;
    private final int propertyId;

    static {
        registerType(getType(), new EntityIterableInstantiator() {
            @Override
            public EntityIterableBase instantiate(PersistentStoreTransaction txn, PersistentEntityStoreImpl store, Object[] parameters) {
                return new PropertiesIterable(txn, Integer.valueOf((String) parameters[0]), Integer.valueOf((String) parameters[1]));
            }
        });
    }

    private static EntityIterableType getType() {
        return EntityIterableType.ENTITIES_WITH_PROPERTY_SORTED_BY_VALUE;
    }

    public int getEntityTypeId() {
        return entityTypeId;
    }

    public PropertiesIterable(@NotNull final PersistentStoreTransaction txn, final int entityTypeId, final int propertyId) {
        super(txn);
        this.entityTypeId = entityTypeId;
        this.propertyId = propertyId;
    }

    @Override
    public boolean isSortedById() {
        return false;
    }

    @Override
    @NotNull
    public EntityIteratorBase getIteratorImpl(@NotNull final PersistentStoreTransaction txn) {
        final PropertiesIterator result = getIterator(txn, true);
        if (result == null) {
            return EntityIteratorBase.EMPTY;
        }
        return result;
    }

    @Override
    public boolean nonCachedHasFastCountAndIsEmpty() {
        return true;
    }

    @NotNull
    @Override
    public EntityIterator getReverseIteratorImpl(@NotNull final PersistentStoreTransaction txn) {
        final PropertiesIterator result = getIterator(txn, false);
        if (result == null) {
            return EntityIteratorBase.EMPTY;
        }
        return result;
    }


    @Override
    @NotNull
    protected EntityIterableHandle getHandleImpl() {
        return new PropertiesIterableHandle();
    }

    @Override
    protected CachedInstanceIterable createCachedInstance(@NotNull final PersistentStoreTransaction txn) {
        return UpdatablePropertiesCachedInstanceIterable.newInstance(txn, getIterator(txn, true), this);
    }

    @Override
    protected long countImpl(@NotNull final PersistentStoreTransaction txn) {
        final Store valueIndex = getStore().getPropertiesTable(txn, entityTypeId).getValueIndex(txn, propertyId, false);
        return valueIndex == null ? 0 : valueIndex.count(txn.getEnvironmentTransaction());
    }

    @Override
    public boolean isEmptyImpl(@NotNull final PersistentStoreTransaction txn) {
        return countImpl(txn) == 0;
    }

    private Cursor openCursor(@NotNull final PersistentStoreTransaction txn) {
        return getStore().getPropertyValuesIndexCursor(txn, entityTypeId, propertyId);
    }

    private PropertiesIterator getIterator(@NotNull final PersistentStoreTransaction txn, final boolean ascending) {
        try (Cursor primaryIndex = getStore().getPrimaryPropertyIndexCursor(txn, entityTypeId)) {
            final Cursor valueIdx = openCursor(txn);
            if (valueIdx == null) {
                return null;
            }
            return new PropertiesIterator(valueIdx, primaryIndex, ascending);
        }
    }

    /**
     * Public access is needed in order to access directly from PersistentStoreTransaction.
     */

    private final class PropertiesIterableHandle extends ConstantEntityIterableHandle {

        public PropertiesIterableHandle() {
            super(PropertiesIterable.this.getStore(), PropertiesIterable.getType());
        }

        @NotNull
        @Override
        public int[] getPropertyIds() {
            return new int[]{propertyId};
        }

        @Override
        public void toString(@NotNull final StringBuilder builder) {
            super.toString(builder);
            builder.append(entityTypeId);
            builder.append('-');
            builder.append(propertyId);
        }

        @Override
        public void hashCode(@NotNull final EntityIterableHandleHash hash) {
            hash.apply(entityTypeId);
            hash.applyDelimiter();
            hash.apply(propertyId);
        }

        @Override
        public int getEntityTypeId() {
            return entityTypeId;
        }

        @Override
        public boolean isMatchedPropertyChanged(@NotNull final EntityId id,
                                                final int propertyId,
                                                @Nullable final Comparable oldValue,
                                                @Nullable final Comparable newValue) {
            return PropertiesIterable.this.propertyId == propertyId && entityTypeId == id.getTypeId();
        }

        @Override
        public boolean onPropertyChanged(@NotNull PropertyChangedHandleChecker handleChecker) {
            UpdatablePropertiesCachedInstanceIterable iterable
                = PersistentStoreTransaction.getUpdatable(handleChecker, this, UpdatablePropertiesCachedInstanceIterable.class);
            if (iterable != null) {
                final Comparable oldValue = handleChecker.getOldValue();
                final Comparable newValue = handleChecker.getNewValue();
                final long localId = handleChecker.getLocalId();
                if (oldValue instanceof ComparableSet || newValue instanceof ComparableSet) {
                    //noinspection ConstantConditions
                    final ComparableSet oldSet = (ComparableSet) oldValue;
                    final ComparableSet newSet = (ComparableSet) newValue;
                    if (oldSet != null) {
                        //noinspection unchecked
                        for (final Comparable item : (Iterable) oldSet.minus(newSet)) {
                            iterable.update(entityTypeId, localId, item, null);
                        }
                    }
                    if (newSet != null) {
                        //noinspection unchecked
                        for (final Comparable item : (Iterable) newSet.minus(oldSet)) {
                            iterable.update(entityTypeId, localId, null, item);
                        }
                    }
                } else {
                    iterable.update(entityTypeId, localId, oldValue, newValue);
                }
                return true;
            }
            return false;
        }
    }

    /**
     * Public access is needed in order to access directly from SortIterator.
     */
    private final class PropertiesIterator extends EntityIteratorBase implements PropertyValueIterator {

        private boolean hasNext;
        private final boolean ascending;
        private final ComparableBinding binding;
        @Nullable
        private ByteIterable currentCursorKey;
        @Nullable
        private Comparable currentValue;

        private PropertiesIterator(@NotNull final Cursor secondaryIndex,
                                   @NotNull final Cursor primaryIndex,
                                   final boolean ascending) {
            super(PropertiesIterable.this);
            setCursor(secondaryIndex);
            this.ascending = ascending;
            //noinspection AssignmentUsedAsCondition
            if (hasNext = getNext(secondaryIndex)) {
                final long entityLocalId = LongBinding.compressedEntryToLong(secondaryIndex.getValue());
                final ByteIterable value = primaryIndex.getSearchKey(
                    PropertyKey.propertyKeyToEntry(new PropertyKey(entityLocalId, propertyId)));
                if ((hasNext = value != null)) {
                    final PropertyValue propertyValue = getStore().getPropertyTypes().entryToPropertyValue(value);
                    if (propertyValue.getType().getTypeId() != ComparableValueType.COMPARABLE_SET_VALUE_TYPE) {
                        binding = propertyValue.getBinding();
                    } else {
                        final Class itemClass = ((ComparableSet) propertyValue.getData()).getItemClass();
                        if (itemClass == null) {
                            throw new NullPointerException("Can't be: null item class for a non-empty ComparableSet");
                        }
                        //noinspection unchecked
                        binding = getStore().getPropertyTypes().getPropertyType(itemClass).getBinding();
                    }
                } else {
                    binding = null;
                }
            } else {
                binding = null;
            }
        }

        @Override
        public boolean hasNextImpl() {
            if (hasNext) {
                final ByteIterable key = getCursor().getKey();
                if (key != currentCursorKey) { // equality check to boost BTreeDup same-key access
                    currentValue = binding.entryToObject(key);
                    currentCursorKey = key;
                }
            } else {
                currentValue = null;
                currentCursorKey = null;
            }
            return hasNext;
        }

        @Override
        @Nullable
        public EntityId nextIdImpl() {
            if (hasNextImpl()) {
                explain(getType());
                final Cursor cursor = getCursor();
                final EntityId result = new PersistentEntityId(entityTypeId, LongBinding.compressedEntryToLong(cursor.getValue()));
                hasNext = getNext(cursor);
                return result;
            }
            return null;
        }

        @Override
        @Nullable
        public Comparable currentValue() {
            return currentValue;
        }

        private boolean getNext(@NotNull final Cursor cursor) {
            return ascending ? cursor.getNext() : cursor.getPrev();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy