/*
* 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;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Xml;
using Lucene.Net.Distributed;
//using Lucene.Net.Distributed.Search;
using Lucene.Net.Distributed.Configuration;
using Documents = Lucene.Net.Documents;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
namespace Lucene.Net.Distributed.Indexing
{
///
/// Definition of configurable search indexes managed by the
/// LuceneUpdater windows service.
///
/// An example configuration would look like the following:
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
public class IndexSet
{
#region Variables
private int _intId = -1;
private string _strLocalPath;
private string _strIdColumn;
private int _intBottomId;
private int _intTopId;
private CurrentIndex _oCurrentIndex;
private IndexAction _eIndexAction=IndexAction.NoAction;
private AnalyzerType _eAnalyzerType=AnalyzerType.StandardAnalyzer;
private Hashtable _htDocuments = new Hashtable();
private Hashtable _htIndexDocuments = new Hashtable();
private List _alFileSystemDocuments = new List();
#endregion
#region Constructors
///
/// Public constructor for IndexSet. An IndexSet is defined in XML configuration
/// and is loaded via a custom configuration handler.
///
/// XmlNode definition for a given IndexSet
public IndexSet(XmlNode node)
{
this.LoadValues(node);
}
#endregion
#region Internal voids
///
/// Internal load method called from the constructor. Loads underlying values
/// based on Xml configuration.
///
/// XmlNode definition for a given IndexSet
internal void LoadValues(XmlNode node)
{
XmlAttributeCollection attributeCollection = node.Attributes;
try
{
this._intId = Convert.ToInt32(attributeCollection["id"].Value);
}
catch (Exception)
{
throw new ConfigurationErrorsException("IndexSet id invalid: " + Environment.NewLine + node.OuterXml);
}
try
{
this._eIndexAction = (IndexAction)Enum.Parse(typeof(IndexAction), attributeCollection["action"].Value);
}
catch (Exception)
{
throw new ConfigurationErrorsException("IndexSet "+this._intId.ToString()+" IndexAction invalid: " + Environment.NewLine + node.OuterXml);
}
try
{
if (attributeCollection["analyzer"] != null)
this._eAnalyzerType = (AnalyzerType)Enum.Parse(typeof(AnalyzerType), attributeCollection["analyzer"].Value);
}
catch (Exception)
{
throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " analyzer invalid: " + Environment.NewLine + node.OuterXml);
}
if (node.ChildNodes.Count==0)
throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " configuration missing " + Environment.NewLine + node.OuterXml);
foreach (XmlNode c in node.ChildNodes)
{
if (!c.HasChildNodes)
{
switch (c.Attributes["key"].Value.ToLower())
{
case "localpath":
this._strLocalPath = c.Attributes["value"].Value;
break;
case "idcolumn":
this._strIdColumn = c.Attributes["value"].Value;
break;
case "bottomid":
try
{
this._intBottomId = Convert.ToInt32(c.Attributes["value"].Value);
}
catch (Exception)
{
throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " bottomid invalid: " + Environment.NewLine + node.OuterXml);
}
break;
case "topid":
try
{
this._intTopId = Convert.ToInt32(c.Attributes["value"].Value);
}
catch (Exception)
{
throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " topid invalid: " + Environment.NewLine + node.OuterXml);
}
break;
}
}
else
{
switch(c.Name.ToLower())
{
case "copy":
if (this._strLocalPath!=null)
LoadCopy(c,this._strLocalPath);
else
LoadCopy(c,node);
break;
}
}
}
this.CheckValidSet(node);
}
internal void LoadCopy(XmlNode node, string localpath)
{
this._oCurrentIndex = new CurrentIndex(node,localpath);
}
internal void LoadCopy(XmlNode node, XmlNode masternode)
{
foreach (XmlNode c in node.ChildNodes)
{
if (c.Attributes["key"] != null)
{
switch (c.Attributes["key"].Value.ToLower())
{
case "localpath":
this.LoadCopy(node, c.Attributes["value"].Value);
break;
}
}
}
}
private void CheckValidSet(XmlNode node)
{
if (this._strLocalPath==null) throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " LocalPath invalid: " + Environment.NewLine + node.OuterXml);
if (this._eIndexAction==IndexAction.NoAction) throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " IndexAction undefined: " + Environment.NewLine + node.OuterXml);
if (this._strIdColumn==null) throw new ConfigurationErrorsException("IndexSet " + this._intId.ToString() + " IdColumn undefined: " + Environment.NewLine + node.OuterXml);
}
#endregion
#region Properties
///
/// Unique identifier for an IndexSet within a configuration of multiple IndexSet objects
///
public int Id
{
get {return this._intId;}
}
///
/// Enumeration dictating the type of updates to be applied to the underlying master index
///
public IndexAction IndexAction
{
get {return this._eIndexAction;}
}
///
/// Enumeration dictating the type of Analyzer to be applied to IndexDocuments in update scenarios
///
public AnalyzerType AnalyzerType
{
get {return this._eAnalyzerType;}
}
///
/// The Analyzer object used in application of IndexDocument updates
///
public Analyzer Analzyer
{
get {return CurrentIndex.GetAnalyzer(this._eAnalyzerType);}
}
///
/// Filesystem path to the master index
///
public string LocalPath
{
get {return this._strLocalPath;}
}
///
/// String name representing the unique key for the given record in the index
///
public string IdColumn
{
get {return this._strIdColumn;}
}
///
/// Minimum IdColumn value for a record in this index
///
public int BottomId
{
get {return this._intBottomId;}
}
///
/// Maximum IdColumn value for a record in this index
///
public int TopId
{
get {return this._intTopId;}
}
///
/// CurrentIndex object associated with this IndexSet. The CurrentIndex is used
/// in determining index settings and maintenance as well as managing physical file updates
/// for index updates.
///
public CurrentIndex CurrentIndex
{
get {return this._oCurrentIndex;}
}
///
/// List of filesystem paths representing files for the master index
///
public List FileSystemDocuments
{
get { return this._alFileSystemDocuments; }
}
///
/// Pending updates to be applied to the master index
///
public Hashtable IndexDocuments
{
get { return this._htIndexDocuments; }
}
///
/// Retrieves the DeleteIndexDocuments from IndexDocuments
///
public Hashtable Documents
{
get
{
this._htDocuments.Clear();
foreach (DictionaryEntry de in this._htIndexDocuments)
{
IndexDocument iDoc = (IndexDocument)de.Value;
if (!(iDoc is DeleteIndexDocument))
this._htDocuments.Add(iDoc.Document, iDoc.GetAnalyzer());
}
return this._htDocuments;
}
}
#endregion
#region Methods
///
/// Retrieves a NameValueCollection of records to first be deleted from an index. Name/Value
/// pair combination consists of IdColumn and RecordId from each IndexDocument in IndexDocuments.
///
///
public NameValueCollection GetDeletionCollection()
{
NameValueCollection nvc = new NameValueCollection(this._htDocuments.Count);
foreach(DictionaryEntry de in this._htIndexDocuments)
nvc.Add(this.IdColumn, ((IndexDocument)de.Value).RecordId.ToString());
return nvc;
}
///
/// Clears the contents of Documents and IndexDocuments
///
public void Reset()
{
this._htIndexDocuments.Clear();
this._htDocuments.Clear();
}
///
/// Executes a Lucene.Net optimization against the referenced index.
///
public void Optimize()
{
if (IndexReader.IndexExists(this._strLocalPath))
{
IndexWriter idxWriter = new IndexWriter(this._strLocalPath, this.Analzyer, false);
idxWriter.SetMergeFactor(2);
idxWriter.Optimize();
idxWriter.Close();
}
}
///
/// Indicates if a given recordId exists within the configuration
/// definition of TopId and BottomId.
///
///
///
public bool ContainsId(int recordId)
{
return (this._intTopId >= recordId && this._intBottomId <= recordId);
}
#endregion
}
}