/*
* 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.model.util;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.LRepository;
import org.lilyproject.repository.api.QName;
import org.lilyproject.repository.api.Record;
import org.lilyproject.repository.api.RecordId;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.TypeManager;
import org.lilyproject.util.Pair;
import org.lilyproject.util.repo.RecordEvent;
public class IndexRecordFilterUtil {
private IndexRecordFilterUtil() {
}
/**
* Returns Record instances that can be used to evaluate an IndexerConf's IndexRecordFilter. The record
* instances are created based on state stored in the RecordEvent by the IndexRecordFilterHook.
*
* <p>The IndexRecordFilterHook puts the fields & record type info needed by each of the
* indexes known at that time in the RecordEvent.</p>
*
* <p>Of course, it can happen that indexerconfs have been changed, or new indexes have been added,
* since the RecordEvent was created. It can also be that the 'new' record state stored in
* the RecordEvent doesn't correspond to the record state you would get now when reading the
* record. It is up to the caller to decide how to deal with these situations.</p>
*
* <p>This method returns always two Record instances: the old and new record state, even if
* there would be no old instance (in case of record creation) or new (in case of record deletion).
* In those cases, the Record objects will be empty, hence will be more generic, and hence
* will only match IndexRecordFilter's that match all records anyway, so they won't match
* more filters than either the old or new record state would match.</p>
*/
public static Pair<Record,Record> getOldAndNewRecordForRecordFilterEvaluation(RecordId recordId, RecordEvent recordEvent,
LRepository repository) throws RepositoryException, InterruptedException {
//
// Create the 'old' and 'new' Record instances.
//
// The RecordEvent contains the fields & record type info needed by the filters of the different
// indexes, this is taken care of by IndexRecordFilterHook.
//
// Of course, it can happen that indexerconfs have been changed, or new indexes have been added,
// since the event was created, and then this information will be missing. We could check for that
// and in case of doubt send the event to the index anyway. This approach however also has the
// disadvantage that, in case there are a lot of outstanding events in the queue, that they
// might be sent to indexes that only expect a low update rate. Besides, it also complicates the
// code. So we go for the simple approach: when the indexerconfs change, there is a transition
// period to be expected, and one might need to rebuild indexes.
//
RecordEvent.IndexRecordFilterData idxSel = recordEvent.getIndexRecordFilterData();
if (idxSel != null) {
Record newRecord = idxSel.getNewRecordExists() ? repository.getRecordFactory().newRecord(recordId) : null;
Record oldRecord = idxSel.getOldRecordExists() ? repository.getRecordFactory().newRecord(recordId) : null;
TypeManager typeManager = repository.getTypeManager();
if (idxSel.getFieldChanges() != null) {
for (RecordEvent.FieldChange fieldChange : idxSel.getFieldChanges()) {
FieldType fieldType = typeManager.getFieldTypeById(fieldChange.getId());
QName name = fieldType.getName();
if (fieldChange.getNewValue() != null) {
Object value = fieldType.getValueType().read(fieldChange.getNewValue());
newRecord.setField(name, value);
}
if (fieldChange.getOldValue() != null) {
Object value = fieldType.getValueType().read(fieldChange.getOldValue());
oldRecord.setField(name, value);
}
}
}
if (idxSel.getNewRecordType() != null) {
newRecord.setRecordType(typeManager.getRecordTypeById(idxSel.getNewRecordType(), null).getName());
}
if (idxSel.getOldRecordType() != null) {
oldRecord.setRecordType(typeManager.getRecordTypeById(idxSel.getOldRecordType(), null).getName());
}
return Pair.create(oldRecord, newRecord);
}
return Pair.create(null, null);
}
}