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

com.hazelcast.query.impl.SortedIndexStore Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.query.impl;

import com.hazelcast.nio.serialization.Data;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * Store indexes rankly.
 */
public class SortedIndexStore extends BaseIndexStore {

    private volatile Map recordsWithNullValue;

    private final ConcurrentSkipListMap> recordMap
            = new ConcurrentSkipListMap>();

    private final IndexFunctor addFunctor;
    private final IndexFunctor removeFunctor;

    public SortedIndexStore(IndexCopyBehavior copyOn) {
        super(copyOn);
        assert copyOn != null;
        if (copyOn == IndexCopyBehavior.COPY_ON_WRITE) {
            addFunctor = new CopyOnWriteAddFunctor();
            removeFunctor = new CopyOnWriteRemoveFunctor();
            recordsWithNullValue = Collections.emptyMap();
        } else {
            addFunctor = new AddFunctor();
            removeFunctor = new RemoveFunctor();
            recordsWithNullValue = new ConcurrentHashMap();
        }
    }

    @Override
    void newIndexInternal(Comparable newValue, QueryableEntry record) {
        addFunctor.invoke(newValue, record);
    }

    @Override
    void removeIndexInternal(Comparable oldValue, Data indexKey) {
        removeFunctor.invoke(oldValue, indexKey);
    }

    @Override
    public void clear() {
        takeWriteLock();
        try {
            recordsWithNullValue.clear();
            recordMap.clear();
        } finally {
            releaseWriteLock();
        }
    }

    @Override
    public Set getSubRecordsBetween(Comparable from, Comparable to) {
        takeReadLock();
        try {
            MultiResultSet results = createMultiResultSet();
            SortedMap> subMap =
                    recordMap.subMap(from, true, to, true);
            for (Map value : subMap.values()) {
                copyToMultiResultSet(results, value);
            }
            return results;
        } finally {
            releaseReadLock();
        }
    }

    @Override
    public Set getSubRecords(ComparisonType comparisonType, Comparable searchedValue) {
        takeReadLock();
        try {
            MultiResultSet results = createMultiResultSet();
            SortedMap> subMap;
            switch (comparisonType) {
                case LESSER:
                    subMap = recordMap.headMap(searchedValue, false);
                    break;
                case LESSER_EQUAL:
                    subMap = recordMap.headMap(searchedValue, true);
                    break;
                case GREATER:
                    subMap = recordMap.tailMap(searchedValue, false);
                    break;
                case GREATER_EQUAL:
                    subMap = recordMap.tailMap(searchedValue, true);
                    break;
                case NOT_EQUAL:
                    // TODO There maybe more efficient way such as
                    // Make a copy of current record map and just remove searched value.
                    // So remaining records are not equal to searched value
                    for (Map.Entry> entry : recordMap.entrySet()) {
                        if (!searchedValue.equals(entry.getKey())) {
                            copyToMultiResultSet(results, entry.getValue());
                        }
                    }
                    return results;
                default:
                    throw new IllegalArgumentException("Unrecognized comparisonType: " + comparisonType);
            }
            for (Map value : subMap.values()) {
                copyToMultiResultSet(results, value);
            }
            return results;
        } finally {
            releaseReadLock();
        }
    }

    @Override
    public Set getRecords(Comparable value) {
        takeReadLock();
        try {
            if (value instanceof IndexImpl.NullObject) {
                return toSingleResultSet(recordsWithNullValue);
            } else {
                return toSingleResultSet(recordMap.get(value));
            }
        } finally {
            releaseReadLock();
        }
    }

    @Override
    public Set getRecords(Set values) {
        takeReadLock();
        try {
            MultiResultSet results = createMultiResultSet();
            for (Comparable value : values) {
                Map records;
                if (value instanceof IndexImpl.NullObject) {
                    records = recordsWithNullValue;
                } else {
                    records = recordMap.get(value);
                }
                if (records != null) {
                    copyToMultiResultSet(results, records);
                }
            }
            return results;
        } finally {
            releaseReadLock();
        }
    }

    /**
     * Adds entry to the given index map without copying it.
     * Needs to be invoked in a thread-safe way.
     *
     * @see IndexCopyBehavior
     */
    private class AddFunctor implements IndexFunctor {
        @Override
        public void invoke(Comparable attribute, QueryableEntry entry) {
            if (attribute instanceof IndexImpl.NullObject) {
                recordsWithNullValue.put(entry.getKeyData(), entry);
            } else {
                Map records = recordMap.get(attribute);
                if (records == null) {
                    records = new ConcurrentHashMap(1, LOAD_FACTOR, 1);
                    recordMap.put(attribute, records);
                }
                records.put(entry.getKeyData(), entry);
            }
        }
    }

    /**
     * Adds entry to the given index map copying it to secure exclusive access.
     * Needs to be invoked in a thread-safe way.
     *
     * @see IndexCopyBehavior
     */
    private class CopyOnWriteAddFunctor implements IndexFunctor {
        @Override
        public void invoke(Comparable attribute, QueryableEntry entry) {
            if (attribute instanceof IndexImpl.NullObject) {
                HashMap copy = new HashMap(recordsWithNullValue);
                copy.put(entry.getKeyData(), entry);
                recordsWithNullValue = copy;
            } else {
                Map records = recordMap.get(attribute);
                if (records == null) {
                    records = Collections.emptyMap();
                }

                records = new HashMap(records);
                records.put(entry.getKeyData(), entry);

                recordMap.put(attribute, records);
            }
        }
    }

    /**
     * Removes entry from the given index map without copying it.
     * Needs to be invoked in a thread-safe way.
     *
     * @see IndexCopyBehavior
     */
    private class RemoveFunctor implements IndexFunctor {
        @Override
        public void invoke(Comparable attribute, Data indexKey) {
            if (attribute instanceof IndexImpl.NullObject) {
                recordsWithNullValue.remove(indexKey);
            } else {
                Map records = recordMap.get(attribute);
                if (records != null) {
                    records.remove(indexKey);
                    if (records.size() == 0) {
                        recordMap.remove(attribute);
                    }
                }
            }
        }
    }

    /**
     * Removes entry from the given index map copying it to secure exclusive access.
     * Needs to be invoked in a thread-safe way.
     *
     * @see IndexCopyBehavior
     */
    private class CopyOnWriteRemoveFunctor implements IndexFunctor {
        @Override
        public void invoke(Comparable attribute, Data indexKey) {
            if (attribute instanceof IndexImpl.NullObject) {
                HashMap copy = new HashMap(recordsWithNullValue);
                copy.remove(indexKey);
                recordsWithNullValue = copy;
            } else {
                Map records = recordMap.get(attribute);
                if (records != null) {
                    records = new HashMap(records);
                    records.remove(indexKey);

                    if (records.isEmpty()) {
                        recordMap.remove(attribute);
                    } else {
                        recordMap.put(attribute, records);
                    }
                }
            }
        }
    }


    @Override
    public String toString() {
        return "SortedIndexStore{"
                + "recordMap=" + recordMap.size()
                + '}';
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy