/* * 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. */ using System; using System.Collections.Generic; using Directory = Lucene.Net.Store.Directory; namespace Lucene.Net.Index { ///

Expert: a MergePolicy determines the sequence of /// primitive merge operations to be used for overall merge /// and optimize operations.

/// ///

Whenever the segments in an index have been altered by /// , either the addition of a newly /// flushed segment, addition of many segments from /// addIndexes* calls, or a previous merge that may now need /// to cascade, invokes /// to give the MergePolicy a chance to pick /// merges that are now required. This method returns a /// instance describing the set of /// merges that should be done, or null if no merges are /// necessary. When IndexWriter.optimize is called, it calls /// and the MergePolicy should /// then return the necessary merges.

/// ///

Note that the policy can return more than one merge at /// a time. In this case, if the writer is using ///, the merges will be run /// sequentially but if it is using /// they will be run concurrently.

/// ///

The default MergePolicy is ///.

/// ///

NOTE: This API is new and still experimental /// (subject to change suddenly in the next release)

/// ///

NOTE: This class typically requires access to /// package-private APIs (e.g. SegmentInfos) to do its job; /// if you implement your own MergePolicy, you'll need to put /// it in package Lucene.Net.Index in order to use /// these APIs. ///

public abstract class MergePolicy : IDisposable { /// OneMerge provides the information necessary to perform /// an individual primitive merge operation, resulting in /// a single new segment. The merge spec includes the /// subset of segments to be merged as well as whether the /// new segment should use the compound file format. /// public class OneMerge { internal SegmentInfo info; // used by IndexWriter internal bool mergeDocStores; // used by IndexWriter internal bool optimize; // used by IndexWriter internal bool registerDone; // used by IndexWriter internal long mergeGen; // used by IndexWriter internal bool isExternal; // used by IndexWriter internal int maxNumSegmentsOptimize; // used by IndexWriter internal SegmentReader[] readers; // used by IndexWriter internal SegmentReader[] readersClone; // used by IndexWriter internal SegmentInfos segments; internal bool useCompoundFile; internal bool aborted; internal System.Exception error; public OneMerge(SegmentInfos segments, bool useCompoundFile) { if (0 == segments.Count) throw new ArgumentException("segments must include at least one segment", "segments"); this.segments = segments; this.useCompoundFile = useCompoundFile; } /// Record that an exception occurred while executing /// this merge /// internal virtual void SetException(System.Exception error) { lock (this) { this.error = error; } } /// Retrieve previous exception set by ///. /// internal virtual System.Exception GetException() { lock (this) { return error; } } /// Mark this merge as aborted. If this is called /// before the merge is committed then the merge will /// not be committed. /// internal virtual void Abort() { lock (this) { aborted = true; } } /// Returns true if this merge was aborted. internal virtual bool IsAborted() { lock (this) { return aborted; } } internal virtual void CheckAborted(Directory dir) { lock (this) { if (aborted) throw new MergeAbortedException("merge is aborted: " + SegString(dir)); } } internal virtual String SegString(Directory dir) { var b = new System.Text.StringBuilder(); int numSegments = segments.Count; for (int i = 0; i < numSegments; i++) { if (i > 0) b.Append(' '); b.Append(segments.Info(i).SegString(dir)); } if (info != null) b.Append(" into ").Append(info.name); if (optimize) b.Append(" [optimize]"); if (mergeDocStores) { b.Append(" [mergeDocStores]"); } return b.ToString(); } public SegmentInfos segments_ForNUnit { get { return segments; } } } /// A MergeSpecification instance provides the information /// necessary to perform multiple merges. It simply /// contains a list of instances. /// public class MergeSpecification { /// The subset of segments to be included in the primitive merge. public IList merges = new List(); public virtual void Add(OneMerge merge) { merges.Add(merge); } public virtual String SegString(Directory dir) { var b = new System.Text.StringBuilder(); b.Append("MergeSpec:\n"); int count = merges.Count; for (int i = 0; i < count; i++) b.Append(" ").Append(1 + i).Append(": ").Append(merges[i].SegString(dir)); return b.ToString(); } } /// Exception thrown if there are any problems while /// executing a merge. /// [Serializable] public class MergeException:System.SystemException { private readonly Directory dir; public MergeException(System.String message, Directory dir):base(message) { this.dir = dir; } public MergeException(System.Exception exc, Directory dir):base(null, exc) { this.dir = dir; } /// Returns the of the index that hit /// the exception. /// public virtual Directory Directory { get { return dir; } } } [Serializable] public class MergeAbortedException:System.IO.IOException { public MergeAbortedException():base("merge is aborted") { } public MergeAbortedException(System.String message):base(message) { } } protected internal IndexWriter writer; protected MergePolicy(IndexWriter writer) { this.writer = writer; } /// Determine what set of merge operations are now necessary on the index. /// calls this whenever there is a change to the segments. /// This call is always synchronized on the instance so /// only one thread at a time will call this method. /// /// /// the total set of segments in the index /// public abstract MergeSpecification FindMerges(SegmentInfos segmentInfos); /// Determine what set of merge operations is necessary in order to optimize /// the index. calls this when its /// method is called. This call is always /// synchronized on the instance so only one thread at a /// time will call this method. /// /// /// the total set of segments in the index /// /// requested maximum number of segments in the index (currently this /// is always 1) /// /// contains the specific SegmentInfo instances that must be merged /// away. This may be a subset of all SegmentInfos. /// public abstract MergeSpecification FindMergesForOptimize(SegmentInfos segmentInfos, int maxSegmentCount, ISet segmentsToOptimize); /// Determine what set of merge operations is necessary in order to expunge all /// deletes from the index. /// /// /// the total set of segments in the index /// public abstract MergeSpecification FindMergesToExpungeDeletes(SegmentInfos segmentInfos); /// Release all resources for the policy. [Obsolete("Use Dispose() instead")] public void Close() { Dispose(); } /// Release all resources for the policy. public void Dispose() { Dispose(true); } protected abstract void Dispose(bool disposing); /// Returns true if a newly flushed (not from merge) /// segment should use the compound file format. /// public abstract bool UseCompoundFile(SegmentInfos segments, SegmentInfo newSegment); /// Returns true if the doc store files should use the /// compound file format. /// public abstract bool UseCompoundDocStore(SegmentInfos segments); } }