Package org.lilyproject.indexer.integration

Source Code of org.lilyproject.indexer.integration.IndexRecordFilterHook

/*
* Copyright 2012 NGDATA nv
*
* 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 org.lilyproject.indexer.integration;

import javax.annotation.PreDestroy;
import java.util.Collection;
import java.util.Set;

import com.google.common.collect.Sets;
import org.lilyproject.indexer.model.api.LResultToSolrMapper;
import org.lilyproject.indexer.model.indexerconf.IndexRecordFilter;
import org.lilyproject.indexer.model.util.IndexInfo;
import org.lilyproject.indexer.model.util.IndexesInfo;
import org.lilyproject.plugin.PluginRegistry;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.FieldTypes;
import org.lilyproject.repository.api.IdentityRecordStack;
import org.lilyproject.repository.api.QName;
import org.lilyproject.repository.api.Record;
import org.lilyproject.repository.api.Repository;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.SchemaId;
import org.lilyproject.repository.api.TypeManager;
import org.lilyproject.repository.spi.RecordUpdateHook;
import org.lilyproject.util.hbase.RepoAndTableUtil;
import org.lilyproject.util.repo.RecordEvent;
import org.lilyproject.util.repo.RecordEvent.IndexRecordFilterData;

/**
* A record update hook that adds information to the RecordEvent needed to evaluate the
* IndexRecordFilter, especially needed to be able to know what IndexRecordFilter's matched
* on the previous (or deleted) record state. Also allows to make this decision without
* needing to read the complete record.
*/
public class IndexRecordFilterHook implements RecordUpdateHook {

    private PluginRegistry pluginRegistry;
    private final IndexesInfo indexesInfo;

    /**
     * Name should be unique among all RecordUpdateHook's. Because the name starts
     * with 'org.lilyproject', this hook does not need to be activated through
     * configuration.
     */
    private final String NAME = "org.lilyproject.IndexRecordFilterHook";

    public IndexRecordFilterHook(IndexesInfo indexesInfo) {
        this.indexesInfo = indexesInfo;
    }

    public IndexRecordFilterHook(PluginRegistry pluginRegistry, IndexesInfo indexesInfo) {
        this.pluginRegistry = pluginRegistry;
        this.indexesInfo = indexesInfo;

        pluginRegistry.addPlugin(RecordUpdateHook.class, NAME, this);
    }

    @PreDestroy
    public void destroy() {
        // Use same arguments as for addPlugin
        pluginRegistry.removePlugin(RecordUpdateHook.class, NAME, this);
    }

    @Override
    public void beforeUpdate(Record record, Record originalRecord, Repository repository, FieldTypes fieldTypes,
            RecordEvent recordEvent) throws RepositoryException, InterruptedException {
        Collection<IndexInfo> indexInfos = indexesInfo.getIndexInfos();
        if (indexInfos.size() > 0) {
            TypeManager typeMgr = repository.getTypeManager();

            RecordEvent.IndexRecordFilterData idxSel = new RecordEvent.IndexRecordFilterData();
            recordEvent.setIndexRecordFilterData(idxSel);

            idxSel.setOldRecordExists(true);
            idxSel.setNewRecordExists(true);

            if (indexesInfo.getRecordFilterDependsOnRecordType()) {
                // Because record type names can change, this is not guaranteed to be the same as what gets
                // stored in the repo, but that doesn't matter as it is only for indexing purposes.
                SchemaId oldRecordTypeId = typeMgr.getRecordTypeByName(originalRecord.getRecordTypeName(), null).getId();
                idxSel.setOldRecordType(oldRecordTypeId);
                // on update, specifying record type is optional
                idxSel.setNewRecordType(record.getRecordTypeName() != null ?
                        typeMgr.getRecordTypeByName(record.getRecordTypeName(), null).getId() : oldRecordTypeId);
            }

            Set<QName> names = indexesInfo.getRecordFilterFieldDependencies();
            for (QName name : names) {
                Object oldValue = null, newValue = null;
                if (record.hasField(name)) {
                    newValue = record.getField(name);
                }
                if (originalRecord.hasField(name)) {
                    oldValue = originalRecord.getField(name);
                }
                if (oldValue != null || newValue != null) {
                    FieldType type = fieldTypes.getFieldType(name);
                    addField(type, oldValue, newValue, idxSel);
                }
            }

            calculateIndexInclusion(repository.getRepositoryName(), recordEvent.getTableName(), originalRecord, record,
                    idxSel);

        }
    }

    @Override
    public void beforeCreate(Record newRecord, Repository repository, FieldTypes fieldTypes, RecordEvent recordEvent)
            throws RepositoryException, InterruptedException {

        Collection<IndexInfo> indexInfos = indexesInfo.getIndexInfos();
        if (indexInfos.size() > 0) {
            TypeManager typeMgr = repository.getTypeManager();

            RecordEvent.IndexRecordFilterData idxSel = new RecordEvent.IndexRecordFilterData();
            recordEvent.setIndexRecordFilterData(idxSel);

            idxSel.setOldRecordExists(false);
            idxSel.setNewRecordExists(true);

            if (indexesInfo.getRecordFilterDependsOnRecordType()) {
                idxSel.setNewRecordType(typeMgr.getRecordTypeByName(newRecord.getRecordTypeName(), null).getId());
            }

            Set<QName> names = indexesInfo.getRecordFilterFieldDependencies();
            for (QName name : names) {
                if (newRecord.hasField(name)) {
                    Object newValue = newRecord.getField(name);
                    FieldType type = fieldTypes.getFieldType(name);
                    addField(type, null, newValue, idxSel);
                }
            }
            calculateIndexInclusion(repository.getRepositoryName(), recordEvent.getTableName(), null, newRecord,
                    idxSel);
        }
    }

    @Override
    public void beforeDelete(Record originalRecord, Repository repository, FieldTypes fieldTypes,
            RecordEvent recordEvent) throws RepositoryException, InterruptedException {
        Collection<IndexInfo> indexInfos = indexesInfo.getIndexInfos();
        if (indexInfos.size() > 0) {
            TypeManager typeMgr = repository.getTypeManager();

            RecordEvent.IndexRecordFilterData idxSel = new RecordEvent.IndexRecordFilterData();
            recordEvent.setIndexRecordFilterData(idxSel);

            idxSel.setOldRecordExists(true);
            idxSel.setNewRecordExists(false);

            if (indexesInfo.getRecordFilterDependsOnRecordType()) {
                idxSel.setOldRecordType(typeMgr.getRecordTypeByName(originalRecord.getRecordTypeName(), null).getId());
            }

            Set<QName> names = indexesInfo.getRecordFilterFieldDependencies();
            for (QName name : names) {
                if (originalRecord.hasField(name)) {
                    Object oldValue = originalRecord.getField(name);
                    FieldType type = fieldTypes.getFieldType(name);
                    addField(type, oldValue, null, idxSel);
                }
            }
            calculateIndexInclusion(repository.getRepositoryName(), recordEvent.getTableName(), originalRecord, null,
                    idxSel);
        }
    }

    private void addField(FieldType type, Object oldValue, Object newValue, RecordEvent.IndexRecordFilterData idxSel)
            throws RepositoryException, InterruptedException {

        if (oldValue == null && newValue == null) {
            return;
        }

        if (oldValue != null) {
            oldValue = type.getValueType().toBytes(oldValue, new IdentityRecordStack());
        }

        if (newValue != null) {
            newValue = type.getValueType().toBytes(newValue, new IdentityRecordStack());
        }

        idxSel.addChangedField(type.getId(), (byte[])oldValue, (byte[])newValue);
    }

    /**
     * Calculate the inclusion/exclusion sets for index subscriptions based on the old and new
     * records, and update the {@code IndexRecordFilterData} with this information.
     *
     * @param table table where the record update has occurred
     * @param oldRecord Previous version of the record, null if the record is being newly created
     * @param newRecord New version of the record, null if the record is being deleted
     * @param indexFilterData To be updated with index subscription inclusion/exclusion information
     */
    void calculateIndexInclusion(String repositoryName, String table,
                                 Record oldRecord, Record newRecord, IndexRecordFilterData indexFilterData) {

        Set<String> applicableIndexes = Sets.newHashSet();
        Set<String> nonApplicableIndexes = Sets.newHashSet();
        for (IndexInfo indexInfo : indexesInfo.getIndexInfos()) {
            String queueSubscriptionId = indexInfo.getIndexDefinition().getSubscriptionId();
            if (indexMatchesRepository(repositoryName, indexInfo) &&
                    indexIsApplicable(indexInfo.getLilyIndexerConf().getRecordFilter(), table, oldRecord, newRecord)) {
                applicableIndexes.add(queueSubscriptionId);
            } else {
                nonApplicableIndexes.add(queueSubscriptionId);
            }
        }

        if (applicableIndexes.isEmpty()) {
            indexFilterData.setSubscriptionExclusions(IndexRecordFilterData.ALL_INDEX_SUBSCRIPTIONS);
        } else if (nonApplicableIndexes.isEmpty()) {
            indexFilterData.setSubscriptionInclusions(IndexRecordFilterData.ALL_INDEX_SUBSCRIPTIONS);
        } else if (applicableIndexes.size() > nonApplicableIndexes.size()) {
            indexFilterData.setSubscriptionInclusions(applicableIndexes);
        } else {
            indexFilterData.setSubscriptionExclusions(nonApplicableIndexes);
        }
    }

    boolean indexMatchesRepository(String repositoryName, IndexInfo indexInfo){
        String indexRepo = indexInfo.getRepositoryName();
        indexRepo = (indexRepo != null ? indexRepo : RepoAndTableUtil.DEFAULT_REPOSITORY);
        return indexRepo.equals(repositoryName);
    }

    /**
     * Determine if an {@code IndexRecordFilter} is applicable for either the old or new version of
     * a record.
     *
     * @return true if the index is applicable for either the new or old version of the record
     */
    boolean indexIsApplicable(IndexRecordFilter filter, String table, Record oldRecord, Record newRecord) {
        return ((oldRecord != null && filter.getIndexCase(table, oldRecord) != null)
                || (newRecord != null && filter.getIndexCase(table, newRecord) != null));
    }

}
TOP

Related Classes of org.lilyproject.indexer.integration.IndexRecordFilterHook

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.