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

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

The newest version!
/**
 * Copyright 2010 - 2018 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.core.dataStructures.hash.IntHashMap;
import jetbrains.exodus.core.dataStructures.hash.LinkedHashSet;
import jetbrains.exodus.core.dataStructures.hash.ObjectProcedure;
import jetbrains.exodus.entitystore.*;
import jetbrains.exodus.entitystore.tables.LinkValue;
import jetbrains.exodus.entitystore.tables.PropertyKey;
import jetbrains.exodus.env.Cursor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Iterator;
import java.util.Set;

@SuppressWarnings({"RawUseOfParameterizedType"})
public class SelectManyDistinctIterable extends EntityIterableDecoratorBase {

    private final int linkId;

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

    public SelectManyDistinctIterable(@NotNull final PersistentStoreTransaction txn,
                                      @NotNull final EntityIterableBase source,
                                      final int linkId) {
        super(txn, source);
        this.linkId = linkId;
    }

    public static EntityIterableType getType() {
        return EntityIterableType.SELECTMANY_DISTINCT;
    }

    @Override
    @NotNull
    public EntityIteratorBase getIteratorImpl(@NotNull final PersistentStoreTransaction txn) {
        return new EntityIteratorFixingDecorator(this, new SelectManyDistinctIterator(txn));
    }

    @Override
    @NotNull
    protected EntityIterableHandle getHandleImpl() {
        return new EntityIterableHandleDecorator(getStore(), SelectManyDistinctIterable.getType(), source.getHandle()) {

            @NotNull
            private final int[] linkIds = mergeFieldIds(new int[]{linkId}, decorated.getLinkIds());

            @NotNull
            @Override
            public int[] getLinkIds() {
                return linkIds;
            }

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

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

            @Override
            public boolean isMatchedEntityAdded(@NotNull final EntityId added) {
                return decorated.isMatchedEntityAdded(added);
            }

            @Override
            public boolean isMatchedEntityDeleted(@NotNull final EntityId deleted) {
                return decorated.isMatchedEntityDeleted(deleted);
            }

            @Override
            public boolean isMatchedLinkAdded(@NotNull final EntityId source, @NotNull final EntityId target, final int linkId) {
                return linkId == SelectManyDistinctIterable.this.linkId || decorated.isMatchedLinkAdded(source, target, linkId);
            }

            @Override
            public boolean isMatchedLinkDeleted(@NotNull final EntityId source, @NotNull final EntityId target, final int linkId) {
                return linkId == SelectManyDistinctIterable.this.linkId || decorated.isMatchedLinkDeleted(source, target, linkId);
            }

            @Override
            public boolean isMatchedPropertyChanged(@NotNull EntityId id,
                                                    final int propertyId,
                                                    @Nullable final Comparable oldValue,
                                                    @Nullable final Comparable newValue) {
                return decorated.isMatchedPropertyChanged(id, propertyId, oldValue, newValue);
            }
        };
    }

    private class SelectManyDistinctIterator extends EntityIteratorBase {

        @NotNull
        private final EntityIteratorBase sourceIt;
        @NotNull
        private final IntHashMap usedCursors;
        private Set usedIds;
        @NotNull
        private final PersistentStoreTransaction txn;

        private SelectManyDistinctIterator(@NotNull final PersistentStoreTransaction txn) {
            super(SelectManyDistinctIterable.this);
            sourceIt = (EntityIteratorBase) source.iterator();
            usedCursors = new IntHashMap<>();
            usedIds = null;
            this.txn = txn;
        }

        @Override
        protected boolean hasNextImpl() {
            if (usedIds == null) {
                collectIds();
            }
            return !usedIds.isEmpty();
        }

        @SuppressWarnings({"ObjectAllocationInLoop"})
        private void collectIds() {
            final Set usedIds = new LinkedHashSet<>();
            this.usedIds = usedIds;
            final EntityIterator sourceIt = this.sourceIt;
            final int linkId = SelectManyDistinctIterable.this.linkId;
            if (linkId >= 0) {
                while (sourceIt.hasNext()) {
                    EntityId nextId = sourceIt.nextId();
                    if (nextId == null) {
                        continue;
                    }
                    final int typeId = nextId.getTypeId();
                    Cursor cursor = usedCursors.get(typeId);
                    if (cursor == null) {
                        cursor = getStore().getLinksFirstIndexCursor(txn, typeId);
                        usedCursors.put(typeId, cursor);
                    }
                    final long sourceLocalId = nextId.getLocalId();
                    ByteIterable value = cursor.getSearchKey(PropertyKey.propertyKeyToEntry(new PropertyKey(sourceLocalId, linkId)));
                    if (value == null) {
                        usedIds.add(null);
                    } else {
                        for (; ; ) {
                            // value is updated automatically through every iteration
                            final LinkValue linkValue = LinkValue.entryToLinkValue(value);
                            nextId = linkValue.getEntityId();
                            usedIds.add(nextId);
                            if (!cursor.getNext()) {
                                break;
                            }
                            final PropertyKey key = PropertyKey.entryToPropertyKey(cursor.getKey());
                            if (key.getPropertyId() != linkId || key.getEntityLocalId() != sourceLocalId) {
                                break;
                            }
                            value = cursor.getValue(); // must be called for XD, because it returns different value pointer
                        }
                    }
                }
            }
        }

        @Override
        @Nullable
        public EntityId nextIdImpl() {
            final Iterator it = usedIds.iterator();
            if (it.hasNext()) {
                final EntityId id = it.next();
                usedIds.remove(id);
                return id;
            }
            return null;
        }

        @Override
        public boolean dispose() {
            sourceIt.disposeIfShouldBe();
            return super.dispose() && usedCursors.forEachValue(new ObjectProcedure() {
                @Override
                public boolean execute(final Cursor object) {
                    object.close();
                    return true;
                }
            });
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy