/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.lucene.search;


import java.io.Closeable;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

Tracks live field values across NRT reader reopens. This holds a map for all updated ids since the last reader reopen. Once the NRT reader is reopened, it prunes the map. This means you must reopen your NRT reader periodically otherwise the RAM consumption of this class will grow unbounded!

NOTE: you must ensure the same id is never updated at the same time by two threads, because in this case you cannot in general know which thread "won".

/** Tracks live field values across NRT reader reopens. * This holds a map for all updated ids since * the last reader reopen. Once the NRT reader is reopened, * it prunes the map. This means you must reopen your NRT * reader periodically otherwise the RAM consumption of * this class will grow unbounded! * * <p>NOTE: you must ensure the same id is never updated at * the same time by two threads, because in this case you * cannot in general know which thread "won". */
// TODO: should this class handle deletions better...? public abstract class LiveFieldValues<S,T> implements ReferenceManager.RefreshListener, Closeable { private volatile Map<String,T> current = new ConcurrentHashMap<>(); private volatile Map<String,T> old = new ConcurrentHashMap<>(); private final ReferenceManager<S> mgr; private final T missingValue;
The missingValue must be non-null.
/** The missingValue must be non-null. */
public LiveFieldValues(ReferenceManager<S> mgr, T missingValue) { this.missingValue = missingValue; this.mgr = mgr; mgr.addListener(this); } @Override public void close() { mgr.removeListener(this); } @Override public void beforeRefresh() throws IOException { old = current; // Start sending all updates after this point to the new // map. While reopen is running, any lookup will first // try this new map, then fallback to old, then to the // current searcher: current = new ConcurrentHashMap<>(); } @Override public void afterRefresh(boolean didRefresh) throws IOException { // Now drop all the old values because they are now // visible via the searcher that was just opened; if // didRefresh is false, it's possible old has some // entries in it, which is fine: it means they were // actually already included in the previously opened // reader. So we can safely clear old here: old = new ConcurrentHashMap<>(); }
Call this after you've successfully added a document to the index, to record what value you just set the field to.
/** Call this after you've successfully added a document * to the index, to record what value you just set the * field to. */
public void add(String id, T value) { current.put(id, value); }
Call this after you've successfully deleted a document from the index.
/** Call this after you've successfully deleted a document * from the index. */
public void delete(String id) { current.put(id, missingValue); }
Returns the [approximate] number of id/value pairs buffered in RAM.
/** Returns the [approximate] number of id/value pairs * buffered in RAM. */
public int size() { return current.size() + old.size(); }
Returns the current value for this id, or null if the id isn't in the index or was deleted.
/** Returns the current value for this id, or null if the * id isn't in the index or was deleted. */
public T get(String id) throws IOException { // First try to get the "live" value: T value = current.get(id); if (value == missingValue) { // Deleted but the deletion is not yet reflected in // the reader: return null; } else if (value != null) { return value; } else { value = old.get(id); if (value == missingValue) { // Deleted but the deletion is not yet reflected in // the reader: return null; } else if (value != null) { return value; } else { // It either does not exist in the index, or, it was // already flushed & NRT reader was opened on the // segment, so fallback to current searcher: S s = mgr.acquire(); try { return lookupFromSearcher(s, id); } finally { mgr.release(s); } } } }
This is called when the id/value was already flushed and opened in an NRT IndexSearcher. You must implement this to go look up the value (eg, via doc values, field cache, stored fields, etc.).
/** This is called when the id/value was already flushed and opened * in an NRT IndexSearcher. You must implement this to * go look up the value (eg, via doc values, field cache, * stored fields, etc.). */
protected abstract T lookupFromSearcher(S s, String id) throws IOException; }