From 5496bcfb04659ff88cdbdfc80a2cc7c004c52a70 Mon Sep 17 00:00:00 2001 From: grob Date: Fri, 29 Sep 2006 13:49:40 +0000 Subject: [PATCH] * this library now expects Lucene version 1.9.1 (due to API changes in Lucene it currently doesn't work with version 2.0) * added (most of) missing analyzers in Search.getAnalyzer() * createIndex doesn't support a third argument for force-create an index anymore. to clear an index use its create() method instead * Index now uses an IndexModifier instead of an IndexWriter in most cases * to create a new Searcher instance now call index.getSearcher() or pass the index to search in as argument to the Searcher constructor * checkWriteLock() is gone, instead getWriter() and getModifier() try for 2 seconds to return a writer/modifier. this should avoid concurrency problems * Index.addIndexes() now takes a number of index directory arguments * Index.create() now deletes all files in the directory (before it didn't) * added checks for existing writer/modifier/reader before calling close() * Index.getFieldNames() now returns a javascript array instead of a java object one * Index.isLocked() doesn't create a reader anymore (before it did, but didn't use it) * removed Index.getTerms() - if someone is missing that, shout * Index.addDocument() now expects a single Document instance, for batch adding use the new method addDocuments() * same with Index.removeDocument() * added new method Index.updateDocument() that does removal and adding in one step (so the index is locked just once instead of two times) * the wrapped lucene Query instance is now a public field - this saves from duplicating the getter method in every extending prototype * Document.addField() now uses new DateTools class instead of the deprecated DateField utility methods * removed Document.getDateField() since Document.getField() now handles dates as well * added JSDoc compatible documentation --- helma/Search.js | 1278 +++++++++++++++++++++++++++-------------------- 1 file changed, 727 insertions(+), 551 deletions(-) diff --git a/helma/Search.js b/helma/Search.js index 9513a568..6cbdf805 100644 --- a/helma/Search.js +++ b/helma/Search.js @@ -17,14 +17,15 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // -// $Revision: 1.2 $ -// $Author: czv $ -// $Date: 2006/04/24 07:02:17 $ +// $Revision: 1.3 $ +// $Author: robert $ +// $Date: 2006/06/15 07:38:19 $ // // take care of any dependencies -app.addRepository('modules/helma/lucene.jar'); +app.addRepository('modules/helma/lucene-core.jar'); +app.addRepository('modules/helma/lucene-analyzers.jar'); if (!global.helma) { @@ -32,63 +33,94 @@ if (!global.helma) { } /** - * Search constructor + * Constructs a new instance of Search. This merely + * checks if the Lucene library is in the application + * classpath. + * @classDescription This class provides functionality for + * creating a search index. + * @return A newly created instance of this prototype. + * @constructor */ helma.Search = function() { - - this.toString = function() { - return helma.Search.toString(); - }; - try { var c = java.lang.Class.forName("org.apache.lucene.analysis.Analyzer", true, app.getClassLoader()); + // FIXME: add additional check for version, since we need >= 1.9 } catch (e) { - throw("helma.Search needs lucene.jar \ + throw("helma.Search needs lucene.jar in version > 1.9 \ in lib/ext or application directory \ [http://lucene.apache.org/]"); } return this; -} +}; -helma.Search.PKG = Packages.org.apache.lucene; -/////////////////////////////////////////// -// static methods -/////////////////////////////////////////// +/********************** + * STATIC METHODS * + **********************/ + helma.Search.toString = function() { return "[helma.Search]"; -} +}; /** - * static method that returns a new Analyzer instance - * depending on the language passed as argument - * @param String language - * @return Object analyzer + * Returns a new Analyzer instance depending on the key + * passed as argument. Currently supported arguments are + * "br" (BrazilianAnalyzer), "cn" (ChineseAnalyzer), "cz" (CzechAnalyzer), + * "nl" (DutchAnalyzer), "fr" (FrenchAnalyzer), "de" (GermanAnalyzer), + * "el" (GreekAnalyzer), "keyword" (KeywordAnalyzer), "ru" (RussianAnalyzer), + * "simple" (SimpleAnalyzer), "snowball" (SnowballAnalyzer), "stop" (StopAnalyzer) + * "whitespace" (WhitespaceAnalyzer). If no argument is given, a StandardAnalyzer + * is returned. + * @param {String} key The key identifying the analyzer + * @return A newly created Analyzer instance + * @type {org.apache.lucene.analysis.Analyzer} */ -helma.Search.getAnalyzer = function(lang) { - switch (lang) { +helma.Search.getAnalyzer = function(key) { + var pkg = Packages.org.apache.lucene; + switch (key) { + case "br": + return new pkg.analysis.br.BrazilianAnalyzer(); + case "cn": + return new pkg.analysis.cn.ChineseAnalyzer(); + case "cz": + return new pkg.analysis.cz.CzechAnalyzer(); + case "nl": + return new pkg.analysis.nl.DutchAnalyzer(); + case "fr": + return new pkg.analysis.fr.FrenchAnalyzer(); case "de": - return new helma.Search.PKG.analysis.de.GermanAnalyzer(); + return new pkg.analysis.de.GermanAnalyzer(); + case "el": + return new pkg.analysis.el.GreekAnalyzer(); + case "keyword": + return new pkg.analysis.KeywordAnalyzer(); case "ru": - return new helma.Search.PKG.analysis.ru.RussianAnalyzer(); - case "si": + return new pkg.analysis.ru.RussianAnalyzer(); case "simple": - return new helma.Search.PKG.analysis.SimpleAnalyzer(); + return new pkg.analysis.SimpleAnalyzer(); + case "snowball": + return new pkg.analysis.snowball.SnowballAnalyzer(); + case "stop": + return new pkg.analysis.StopAnalyzer(); case "whitespace": - return new helma.Search.PKG.analysis.WhitespaceAnalyzer(); + return new pkg.analysis.WhitespaceAnalyzer(); default: - return new helma.Search.PKG.analysis.standard.StandardAnalyzer(); + return new pkg.analysis.standard.StandardAnalyzer(); } }; /** - * constructor for QueryFilter objects - * @param Object instance of Search.Query + * Constructs a new QueryFilter instance. This class + * wraps a lucene QueryFilter. + * @param {helma.Search.Query} q The query object to use as + * the basis for the QueryFilter instance. + * @return A newly created QueryFilter instance. + * @constructor */ helma.Search.QueryFilter = function(q) { - var wrappedFilter = new helma.Search.PKG.search.QueryFilter(q.getQuery()); + var wrappedFilter = new Packages.org.apache.lucene.search.QueryFilter(q.getQuery()); this.getFilter = function() { return wrappedFilter; @@ -102,15 +134,22 @@ helma.Search.QueryFilter = function(q) { }; -/////////////////////////////////////////// -// prototype methods -/////////////////////////////////////////// +/************************* + * PROTOTYPE METHODS * + *************************/ + + +/** @ignore */ +helma.Search.prototype.toString = function() { + return helma.Search.toString(); +}; /** - * returns an instance of org.apache.lucene.store.FSDirectory - * @param Object an instance of File, helma.File, java.io.File or the - * path to the directory as String - * @return Object org.apache.lucene.store.FSDirectory + * Returns an instance of org.apache.lucene.store.FSDirectory + * @param {File | helma.File | java.io.File | String} dir The directory + * where the index is located or should be created at. + * @return The index directory. + * @type org.apache.lucene.store.FSDirectory */ helma.Search.prototype.getDirectory = function(dir, create) { if (!dir) { @@ -124,16 +163,16 @@ helma.Search.prototype.getDirectory = function(dir, create) { } else if (!((d = dir) instanceof java.io.File)) { throw("helma.Search.getDirectory(): " + dir + " is not a valid argument."); } - return helma.Search.PKG.store.FSDirectory.getDirectory(d, + return Packages.org.apache.lucene.store.FSDirectory.getDirectory(d, create == true ? true : !d.exists()); -} +}; /** - * returns an instance of org.apache.lucene.store.RAMDirectory - * @param Object the directory to create the RAMDirectory from. Argument - * can be an instance of File, helma.File, java.io.File or the - * path to the directory as String - * @return Object org.apache.lucene.store.RAMDirectory + * Returns a RAM directory object. + * @param {File | helma.File | java.io.File | String} dir Optional directory + * containing a Lucene index from which this RAM directory should be created. + * @return A RAM directory instance. + * @type org.apache.lucene.store.RAMDirectory */ helma.Search.prototype.getRAMDirectory = function(dir) { if (dir != null) { @@ -148,176 +187,143 @@ helma.Search.prototype.getRAMDirectory = function(dir) { if (!d.exists()) { throw("helma.Search.getRAMDirectory(): " + dir + " does not exist."); } - return helma.Search.PKG.store.RAMDirectory(d); + return Packages.org.apache.lucene.store.RAMDirectory(d); } - return helma.Search.PKG.store.RAMDirectory(); -} + return Packages.org.apache.lucene.store.RAMDirectory(); +}; /** - * returns a new index Object - * @param Object directory containing the index (an instance of either - * org.apache.lucene.store.FSDirectory or org.apache.lucene.store.RAMDirectory) - * @param Object (optional) Analyzer to use (instance of org.apache.lucene.analysis.Analyzer) - * @param Boolean (optional) Flag to force index creation - * @return Object Index object + * Creates a new Lucene index in the directory passed as + * argument, using an optional analyzer, and returns an instance + * of helma.Search.Index. Any already existing index in this + * directory will be preserved. + * @param {org.apache.lucene.store.Directory} dir The directory + * where the index should be stored. This can be either a + * FSDirectory or a RAMDirectory instance. + * @param {org.apache.lucene.analysis.Analyzer} analyzer The analyzer to + * use for the index. If not specified a StandardAnalyzer will be used. + * @return The index instance. + * @type helma.Search.Index */ -helma.Search.prototype.createIndex = function(dir, analyzer, forceCreate) { - if (arguments.length == 0) { - throw("helma.Search.createIndex(): insufficient arguments."); - } else if (arguments.length == 1) { +helma.Search.prototype.createIndex = function(dir, analyzer) { + if (!dir || !(dir instanceof Packages.org.apache.lucene.store.Directory)) { + throw("Index directory missing or invalid."); + } else if (!analyzer) { + // no analyzer given, use a StandardAnalyzer analyzer = helma.Search.getAnalyzer(); - forceCreate = false; - } else if (arguments.length == 2) { - if (arguments[1].constructor == Boolean) { - analyzer = helma.Search.getAnalyzer(); - forceCreate = arguments[1]; - } else { - forceCreate = false; - } } var index = new helma.Search.Index(dir, analyzer); - if (!helma.Search.PKG.index.IndexReader.indexExists(dir) || forceCreate == true) { + if (!Packages.org.apache.lucene.index.IndexReader.indexExists(dir)) { index.create(); } return index; }; -/////////////////////////////////////////// -// Index -/////////////////////////////////////////// +/************* + * INDEX * + *************/ /** - * static constructor for Index objects - * @param Object index directory, either an instance of FSDirectory or RAMDirectory - * @param Object Lucene analyzer object + * @class Instances of this class represent a Lucene search index + * located in either a directory on disk or in RAM. This class + * provides various methods for modifying the underlying Lucene index. + * @param {org.apache.lucene.store.Directory} directory The directory + * where the Lucene index is located at. + * @param {org.apache.lucene.analysis.Analyzer} analyzer The analyzer + * to use when modifying the index. + * @constructor */ helma.Search.Index = function(directory, analyzer) { + /** - * returns an IndexWriter object - * @param Boolean if true a new index is created + * Tries to create a new IndexWriter or IndexModifier for up to the + * number of milliseconds defined in helma.Search.Index.LOCK_TIMEOUT. + * @private + */ + var getWriterModifier = function(what, create) { + var waitFor, elapsed = 0; + while (elapsed < helma.Search.Index.LOCK_TIMEOUT) { + if (!this.isLocked()) { + return new Packages.org.apache.lucene.index[what](directory, + analyzer, create == true ? true : false); + } else { + waitFor = Math.round(Math.random() * 100) + 50; + java.lang.Thread.sleep(waitFor); + elapsed += waitFor; + app.logger.debug("[Thread " + java.lang.Thread.currentThread().getId() + + "] waiting for " + elapsed + "ms"); + } + } + throw("Failed to create " + what + " due to active lock (Thread " + + java.lang.Thread.currentThread().getId() + ")"); + }; + + /** + * Returns an IndexModifier instance for adding or deleting + * documents to resp. from the underlying index. If the + * index is currently locked this method will try for the next + * two seconds to create the IndexModifier, otherwise it will + * throw an error. + * @return An IndexModifier instance. + * @type org.apache.lucene.index.IndexModifier + */ + this.getModifier = function(create) { + return getWriterModifier.call(this, "IndexModifier", create); + }; + + /** + * Returns an IndexWriter instance that can be used to add documents to + * the underlying index or to perform various other modifying operations. + * If the index is currently locked this method will try for the next + * two seconds to create the IndexWriter, otherwise it will + * throw an error. + * @return An IndexWriter instance. + * @type org.apache.lucene.index.IndexWriter */ this.getWriter = function(create) { - return new helma.Search.PKG.index.IndexWriter(directory, - analyzer, create == true ? true : false); + return getWriterModifier.call(this, "IndexWriter", create); }; /** - * returns an IndexReader object + * Returns an IndexReader instance. Due to locking issues an + * IndexModifier should be used for deleting documents. + * @return An IndexReader instance + * @type org.apache.lucene.index.IndexReader */ this.getReader = function() { - return helma.Search.PKG.index.IndexReader.open(directory); + return Packages.org.apache.lucene.index.IndexReader.open(directory); }; /** - * return the directory of the index - * @param Object helma.File object representing the index' directory + * Returns the directory the underlying Lucene index is located at. + * @return The directory of this index + * @type org.apache.lucene.store.Directory */ this.getDirectory = function() { return directory; }; /** - * constructor function for Searcher objects + * Returns the analyzer used within this index. + * @return The analyzer used within this index. + * @type org.apache.lucene.analysis.Analyer */ - this.Searcher = function() { - var s = new helma.Search.PKG.search.IndexSearcher(directory); - var sortFields; - - /** - * wraps a org.lucene.search.Hits collection - * @param Object instance of org.lucene.search.Hits - */ - var HitCollection = function(hits) { - /** - * silently converts the requested hit into - * an instance of helma.Search.Document - * @param Int index position of hit in collection - * @return instance of helma.Search.Document - */ - this.get = function(idx) { - var doc = new helma.Search.Document(hits.doc(idx)); - doc.score = hits.score(idx); - doc.rank = idx +1; - return doc; - } - - /** - * returns the nr. of hits - */ - this.size = this.length = function() { - return (hits != null) ? hits.length() : 0; - } - return this; - } - - this.hits = null; - - /** - * main search method. the resulting hits collection is - * stored in the property hits. don't forget to do a close() - * when finished processing the resulting hits, otherwise - * the index files will stay locked! - * @param Object instance of Search.Query - * @param Object instance of QueryFilter - * @return Int number of hits - */ - this.search = function(query, filter) { - var pkg = helma.Search.PKG.search; - var hits; - if (sortFields) { - var arr = java.lang.reflect.Array.newInstance(pkg.SortField, sortFields.size()); - var sort = pkg.Sort(sortFields.toArray(arr)); - if (filter) { - hits = s.search(query.getQuery(), filter.getFilter(), sort); - } else { - hits = s.search(query.getQuery(), sort); - } - } else if (filter) { - hits = s.search(query.getQuery(), filter.getFilter()); - } else { - hits = s.search(query.getQuery()); - } - this.hits = new HitCollection(hits); - return this.hits.length(); - }; - - /** - * sets a field as result sorting field - */ - this.sortBy = function(field) { - var pkg = helma.Search.PKG.search; - if (!sortFields) - sortFields = new java.util.Vector(); - var f = field; - var t = pkg.SortField.AUTO; - var r = false; - if (arguments.length == 3) { - t = pkg.SortField[arguments[1].toUpperCase()]; - r = arguments[2]; - } else if (arguments.length == 2) { - if (arguments[1].constructor == Boolean) { - r = arguments[1]; - } else { - t = pkg.SortField[arguments[1].toUpperCase()]; - } - } - sortFields.add(new pkg.SortField(f, t, r)); - return; - }; - - /** - * closes the wrapped IndexSearcher - */ - this.close = function() { - s.close(); - return; - }; - - return this; + this.getAnalyzer = function() { + return analyzer; }; - + + /** + * Returns a searcher for querying this index. + * @return A searcher useable for querying the index. + * @type helma.Search.Searcher + */ + this.getSearcher = function() { + return new helma.Search.Searcher(this); + }; + + /** @ignore */ this.toString = function() { return ("[Lucene Index " + directory + "]"); }; @@ -326,314 +332,501 @@ helma.Search.Index = function(directory, analyzer) { }; /** - * check if the index is locked. if true wait a bit - * and return it again until the timeout is reached + * Constant containing the number of milliseconds + * the index will try to create an IndexModifier or IndexWriter + * in case it is currently locked. + * @type Number */ -helma.Search.Index.prototype.checkWriteLock = function() { - var interval = 500; - var timeout = 5000; - var isLocked = helma.Search.PKG.index.IndexReader.isLocked(this.getDirectory()); - if (isLocked) { - var elapsed = 0; - while (elapsed < timeout) { - java.lang.Thread.sleep(interval); - elapsed += interval; - isLocked = helma.Search.PKG.index.IndexReader.isLocked(this.getDirectory()); - if (!isLocked) - return; +helma.Search.Index.LOCK_TIMEOUT = 2000; + +/** + * Merges the indexes passed as argument into this one. + * @param {org.apache.lucene.store.Directory} dir At least one + * index director to add to this index. + */ +helma.Search.Index.prototype.addIndexes = function(dir /* [, dir[, dir...] */) { + var dirs = java.lang.reflect.Array.newInstance(helma.Search.PKG.store.Directory, + arguments.length); + for (var i=0;i 0) { + var arr = java.lang.reflect.Array.newInstance(PKG.SortField, this.sortFields.size()); + var sort = PKG.Sort(this.sortFields.toArray(arr)); + if (filter) { + hits = this.getSearcher().search(query.getQuery(), filter.getFilter(), sort); + } else { + hits = this.getSearcher().search(query.getQuery(), sort); + } + } else if (filter) { + hits = this.getSearcher().search(query.getQuery(), filter.getFilter()); + } else { + hits = this.getSearcher().search(query.getQuery()); + } + this.hits = new helma.Search.HitCollection(hits); + return this.hits.length(); +}; + +/** + * Sets a field as result sorting field. This method can be called + * with a different number of arguments: + * sortBy(fieldName) + * sortBy(fieldName, type) + * sortBy(fieldName, reverse) + * sortBy(fieldName, type, reverse) + * @param {String} fieldName The name of the field in the index by which + * the search result should be ordered. + * @param {String} type The type of the field defined by argument fieldName. + * Valid arguments are "string", "float", "int", "score", "doc", "auto", "custom". + * Default is "auto". See http://lucene.apache.org/java/docs/api/org/apache/lucene/search/SortField.html + * for an explanation. + */ +helma.Search.Searcher.prototype.sortBy = function(fieldName /** type, reverse */) { + var PKG = Packages.org.apache.lucene.search; + if (!this.sortFields) + this.sortFields = new java.util.Vector(); + var f = fieldName; + var t = PKG.SortField.AUTO; + var r = false; + if (arguments.length == 3) { + t = PKG.SortField[arguments[1].toUpperCase()]; + r = arguments[2]; + } else if (arguments.length == 2) { + if (arguments[1].constructor == Boolean) { + r = arguments[1]; + } else { + t = PKG.SortField[arguments[1].toUpperCase()]; + } + } + this.sortFields.add(new PKG.SortField(f, t, r)); + return; +}; + +/** + * Closes the wrapped IndexSearcher instance. + */ +helma.Search.Searcher.prototype.close = function() { + var s = this.getSearcher(); + if (s != null) { + s.close(); + } + return; +}; + +/** + * @class This class provides Helma-like methods for accessing + * a collection of search hits. + * @param {org.lucene.search.Hits} hits The hit collection returned + * by lucene. + * @constructor + */ +helma.Search.HitCollection = function(hits) { + /** + * Silently converts the hit at the given index position into + * an instance of helma.Search.Document. + * @param {Number} idx The index position of the hit + * @return The document object at the given index position + * @type helma.Search.Document + */ + this.get = function(idx) { + var doc = new helma.Search.Document(hits.doc(idx)); + doc.score = hits.score(idx); + doc.rank = idx +1; + return doc; }; - this.setBoost = function(fact) { - this.getQuery().setBoost(fact); - return; + + /** + * Returns the number of hits in this collection. + * @return The number of hits. + * @type Number + */ + this.size = this.length = function() { + return (hits != null) ? hits.length() : 0; }; + + return this; +}; + + +/************************** + * QUERY CONSTRUCTORS * + **************************/ + +/** + * @class Base class for the various query constructors. Don't + * call this directly, as it provides just basic methods used + * in all of its extends. + * @constructor + */ +helma.Search.Query = function() { + /** + * The wrapped query instance + * @type org.apache.lucene.search.Query + * @private + */ + this.query = null; + + return this; +}; + +/** + * Returns the wrapped Lucene Query object. + * @return The wrapped query object + * @type org.apache.lucene.search.Query + */ +helma.Search.Query.prototype.getQuery = function() { + return this.query; +}; + +/** @ignore */ +helma.Search.Query.prototype.toString = function(field) { + return "[" + this.getQuery().toString(field) + "]"; +}; + +/** + * Returns the boost factor of this query. + * @type Number + */ +helma.Search.Query.prototype.getBoost = function() { + return this.getQuery().getBoost(); +}; + +/** + * Sets the boost factor of this query clause to + * the given number. Documents matching this query + * will have their score multiplied with the given + * factor + * @param {Number} fact The factor to multiply the score + * of matching documents with. + */ +helma.Search.Query.prototype.setBoost = function(fact) { + this.getQuery().setBoost(fact); + return; }; /** * Term Query constructor - * @param String name of the field - * @param String query string - * @return Object TermQuery object + * @class This class represents a simple Term Query. + * @param {String} field The name of the field + * @param {String} str The value of the field + * @constructor + * @extends helma.Search.Query */ helma.Search.TermQuery = function(field, str) { - var t = new helma.Search.PKG.index.Term(field, str); - var wrappedQuery = new helma.Search.PKG.search.TermQuery(t); - - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; + var t = new Packages.org.apache.lucene.index.Term(field, str); + this.query = new Packages.org.apache.lucene.search.TermQuery(t); return this; }; -helma.Search.TermQuery.prototype = new helma.Search.QueryBase; +helma.Search.TermQuery.prototype = new helma.Search.Query; /** * Boolean Query constructor + * @class This class represents a Boolean Query, providing + * various methods for combining other Query instances using boolean operators. * @param String name of the field * @param String query string * @return Object BooleanQuery object + * @constructor + * @extends helma.Search.Query */ helma.Search.BooleanQuery = function(field, str, clause, analyzer) { - var wrappedQuery = new helma.Search.PKG.search.BooleanQuery(); + this.query = new Packages.org.apache.lucene.search.BooleanQuery(); /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - - /** - * directly call addTerm if constructor was - * called with arguments + * Main constructor body */ if (field && str) { this.addTerm.apply(this, arguments); @@ -641,40 +834,36 @@ helma.Search.BooleanQuery = function(field, str, clause, analyzer) { return this; }; -helma.Search.BooleanQuery.prototype = new helma.Search.QueryBase; +helma.Search.BooleanQuery.prototype = new helma.Search.Query; /** - * add a term to the wrappedQuery object. this method can be called + * Adds a term to the wrapped query object. This method can be called * with two, three or four arguments, eg.: * addTerm("fieldname", "querystring") * addTerm("fieldname", "querystring", "and") * addTerm("fieldname", "querystring", helma.Search.getAnalyzer("de")) * addTerm("fieldname", "querystring", "not", helma.Search.getAnalyzer("simple")) - * - * @param Object either a String or an Array containing Strings - * that determine the index field(s) to match - * @param String string to match - * @param String boolean clause ("or"|"not", default is "and") - * @param Object instance of analysis.Analyzer + * @param {String | Array} field Either a String or an Array containing Strings + * that determine the index field(s) to match + * @param {String} str Query string to match + * @param {String} clause Boolean clause ("or", "not" or "and", default is "and") + * @param {org.apache.lucene.analysis.Analyzer} analyzer An analyzer to use */ helma.Search.BooleanQuery.prototype.addTerm = function(field, str, clause, analyzer) { - if (arguments.length == 3 && arguments[2] instanceof helma.Search.PKG.analysis.Analyzer) { + if (arguments.length == 3 && arguments[2] instanceof Packages.org.apache.lucene.analysis.Analyzer) { analyzer = arguments[2]; clause = "or"; } - if (!analyzer) + if (!analyzer) { analyzer = helma.Search.getAnalyzer(); - - var q; - try { - if (field instanceof Array) - q = helma.Search.PKG.queryParser.MultiFieldQueryParser.parse(str, field, analyzer); - else - q = helma.Search.PKG.queryParser.QueryParser.parse(str, field, analyzer); - } catch (e) { - return; } + var q; + if (field instanceof Array) { + q = Packages.org.apache.lucene.queryParser.MultiFieldQueryParser.parse(str, field, analyzer); + } else { + q = Packages.org.apache.lucene.queryParser.QueryParser.parse(str, field, analyzer); + } switch (clause) { case "or": this.getQuery().add(q, false, false); @@ -689,10 +878,9 @@ helma.Search.BooleanQuery.prototype.addTerm = function(field, str, clause, analy }; /** - * "merge" a query object with a query object passed as - * argument - * @param Object Query object - * @param String boolean clause ("or"|"not", default is "and") + * Adds an additional query clause to this query. + * @param {helma.Search.Query} q The query to add + * @param {String} clause Boolean clause ("or", "not", or "and", default is "and") */ helma.Search.BooleanQuery.prototype.addQuery = function(q, clause) { switch (clause) { @@ -709,145 +897,128 @@ helma.Search.BooleanQuery.prototype.addQuery = function(q, clause) { }; /** - * Phrase Query constructor - * @param String name of the field - * @param String query string - * @return Object PhraseQuery object + * Constructs a new PhraseQuery instance that wraps a lucene Phrase + * Query object. + * @class Instances of this class represent a phrase query. + * @param {String} field The name of the field + * @param {String} str The phrase query string + * @return A newly created PhraseQuery instance + * @constructor + * @extends helma.Search.Query */ helma.Search.PhraseQuery = function(field, str) { - var wrappedQuery = new helma.Search.PKG.search.PhraseQuery(); + this.query = new Packages.org.apache.lucene.search.PhraseQuery(); /** * add a term to the end of the phrase query */ this.addTerm = function(field, str) { - var t = new helma.Search.PKG.index.Term(field, str); - wrappedQuery.add(t); + var t = new Packages.org.apache.lucene.index.Term(field, str); + this.query.add(t); return; }; - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - if (field && str) this.addTerm(field, str); delete this.base; return this; }; -helma.Search.PhraseQuery.prototype = new helma.Search.QueryBase; +helma.Search.PhraseQuery.prototype = new helma.Search.Query; /** - * Range Query constructor - * @param String name of the field - * @param String min value (can be null) - * @param String max value (can be null) - * @param Boolean if true min/max values are included - * @return Obj JS wrapper object + * Constructs a new RangeQuery instance. + * @class Instances of this class represent a range + * query + * @param {String} field The name of the field + * @param {String} from The minimum value to match (can be null) + * @param {String} to The maximum value to match (can be null) + * @param {Boolean} inclusive If true the given min/max values are included + * @return A newly created RangeQuery instance + * @constructor + * @extends helma.Search.Query */ helma.Search.RangeQuery = function(field, from, to, inclusive) { if (!field) throw("Missing field name in RangeQuery()"); if (arguments.length < 4) inclusive = true; - var t1 = from ? new helma.Search.PKG.index.Term(field, from) : null; - var t2 = to ? new helma.Search.PKG.index.Term(field, to) : null; - var wrappedQuery = new helma.Search.PKG.search.RangeQuery(t1, t2, inclusive); - - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - + var t1 = from ? new Packages.org.apache.lucene.index.Term(field, from) : null; + var t2 = to ? new Packages.org.apache.lucene.index.Term(field, to) : null; + this.query = new Packages.org.apache.lucene.search.RangeQuery(t1, t2, inclusive); return this; }; -helma.Search.RangeQuery.prototype = new helma.Search.QueryBase; +helma.Search.RangeQuery.prototype = new helma.Search.Query; /** - * Fuzzy Query constructor - * @param String name of the field - * @param String query string - * @return Object FuzzyQuery object + * Constructs a new FuzzyQuery instance + * @class Instances of this class represent a fuzzy query + * @param {String} field The name of the field + * @param {String} str The query string to match + * @return A newly created FuzzyQuery instance + * @constructor + * @extends helma.Search.Query */ helma.Search.FuzzyQuery = function(field, str) { - var t = new helma.Search.PKG.index.Term(field, str); - var wrappedQuery = new helma.Search.PKG.search.FuzzyQuery(t); - - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - + var t = new Packages.org.apache.lucene.index.Term(field, str); + this.query = new Packages.org.apache.lucene.search.FuzzyQuery(t); return this; }; -helma.Search.FuzzyQuery.prototype = new helma.Search.QueryBase; +helma.Search.FuzzyQuery.prototype = new helma.Search.Query; /** - * Prefix Query constructor - * @param String name of the field - * @param String query string - * @return Object PrefixQuery object + * Constructs a new PrefixQuery instance. + * @class Instances of this class represent a prefix query + * @param {String} field The name of the field + * @param {String} str The query string to match + * @return A newly created PrefixQuery instance + * @constructor + * @extends helma.Search.Query */ helma.Search.PrefixQuery = function(field, str) { - var t = new helma.Search.PKG.index.Term(field, str); - var wrappedQuery = new helma.Search.PKG.search.PrefixQuery(t); - - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - + var t = new Packages.org.apache.lucene.index.Term(field, str); + this.query = new Packages.org.apache.lucene.search.PrefixQuery(t); return this; }; -helma.Search.PrefixQuery.prototype = new helma.Search.QueryBase; +helma.Search.PrefixQuery.prototype = new helma.Search.Query; /** - * Wildcard Query constructor - * @param String name of the field - * @param String query string - * @return Object WildcardQuery object + * Constructs a new WildcardQuery instance. + * @class Instances of this class represent a wildcard query. + * @param {String} field The name of the field + * @param {String} str The query string to match + * @return A newly created WildcardQuery instance + * @constructor + * @extends helma.Search.Query */ helma.Search.WildcardQuery = function(field, str) { - var t = new helma.Search.PKG.index.Term(field, str); - var wrappedQuery = new helma.Search.PKG.search.WildcardQuery(t); - - /** - * getter for wrapped java object - */ - this.getQuery = function() { - return wrappedQuery; - }; - + var t = new Packages.org.apache.lucene.index.Term(field, str); + this.query = new Packages.org.apache.lucene.search.WildcardQuery(t); return this; }; -helma.Search.WildcardQuery.prototype = new helma.Search.QueryBase; +helma.Search.WildcardQuery.prototype = new helma.Search.Query; -/////////////////////////////////////////// -// Document -/////////////////////////////////////////// +/**************** + * DOCUMENT * + ****************/ + /** - * constructor function for Document objects that wrap - * a Lucene Document object - * @param Object (optional) Lucene Document object + * @class Instances of this class represent a single + * index document. This class provides various methods for + * adding content to such documents. + * @param {org.apache.lucene.document.Document} document Optional Lucene Document object + * that should be wrapped by this Document instance. + * @constructor */ helma.Search.Document = function(document) { if (!document) - document = new helma.Search.PKG.document.Document(); + document = new Packages.org.apache.lucene.document.Document(); /** - * return the Lucene Document object wrapped - * by this javascript Document object + * Returns the wrapped Lucene Document object + * @return The wrapped Document object + * @type org.apache.lucene.document.Document */ this.getDocument = function() { return document; @@ -857,77 +1028,77 @@ helma.Search.Document = function(document) { }; /** - * adds a field to this document. - * @param String Name of the field - * @param String Value of the field - * @param Object Parameter object (optional) containing - * the following properties: - * .store (Boolean) - * .index (Boolean) - * .tokenize (Boolean) + * Adds a field to this document. + * @param {String} name The name of the field + * @param {String} value The value of the field + * @param {Object} param Optional parameter object containing the following properties: + * */ helma.Search.Document.prototype.addField = function(name, value, param) { - if (!param) - param = {store: true, index: true, tokenize: true}; - if (value === null) - value = ""; - // if value is a date convert it - if (value instanceof Date) - value = helma.Search.PKG.document.DateField.timeToString(value.getTime()); - var f = new helma.Search.PKG.document.Field(String(name), - String(value), - param.store, - param.index, - param.tokenize); - this.getDocument().add(f); + if (!param) { + param = {}; + } + if (value != null) { + var pkg = Packages.org.apache.lucene.document; + if (value.constructor == Date) { + // Convert the value + value = pkg.DateTools.timeToString(value.getTime(), + pkg.DateTools.Resolution.MINUTE); + } else if (value.constructor != String) { + value = String(value); + } + var f = new pkg.Field(String(name), + value, + param.store || true, + param.index || true, + param.tokenize || true); + this.getDocument().add(f); + } return; }; /** - * return a single document field - * @param String name of the field - * @return Object containing the following parameters: - * .name (String) name of the Field - * .boost (Int) boost factor - * .indexed (Boolean) true if Field is indexed, false otherwise - * .stored (Boolean) true if Field is stored, false otherwise - * .tokenized (Boolean) true if Field is tokenized, false otherwise - * .value (String) value of the Field + * Returns a single document field. + * @param {String} name The name of the field in this document object. + * @return An object containing the following properties: + * + * @type Object */ helma.Search.Document.prototype.getField = function(name) { var f = this.getDocument().getField(name); if (f != null) { - return ({name: name, - boost: f.getBoost(), - indexed: f.isIndexed(), - stored: f.isStored(), - tokenized: f.isTokenized(), - value: f.stringValue()}); - } else { - return null; + var result = {name: name, + boost: f.getBoost(), + indexed: f.isIndexed(), + stored: f.isStored(), + tokenized: f.isTokenized(), + value: null}; + try { + var pkg = Packages.org.apache.lucene.document.DateTools; + result.value = pkg.stringToDate(f.stringValue()); + } catch (e) { + result.value = f.stringValue(); + } + return result; } + return null; }; /** - * return a single document field as Date Object - * @param String name of the field - */ -helma.Search.Document.prototype.getDateField = function(name) { - var f = this.getDocument().getField(name); - if (f != null) { - return ({name: name, - boost: f.getBoost(), - indexed: f.isIndexed(), - stored: f.isStored(), - tokenized: f.isTokenized(), - value: new Date(helma.Search.PKG.document.DateField.stringToTime(f.stringValue()))}); - } else { - return null; - } -}; - -/** - * return the fields of a document + * Returns the fields of a document object. + * @return An array containing all fields in this document object. + * @type Array */ helma.Search.Document.prototype.getFields = function() { var e = this.getDocument().fields(); @@ -939,28 +1110,33 @@ helma.Search.Document.prototype.getFields = function() { }; /** - * returns the boost factor of a document + * Returns the boost factor of a document. + * @return The boost factor of a document + * @type Number */ helma.Search.Document.prototype.getBoost = function() { return this.getDocument().getBoost(); }; /** - * sets the boost factor of a document + * Sets the boost factor of a document. + * @param {Number} boost The boost factor of the document */ helma.Search.Document.prototype.setBoost = function(boost) { this.getDocument().setBoost(boost); return; }; +/** @ignore */ helma.Search.Document.prototype.toString = function() { return "[Document Object]"; }; -/////////////////////////////////////////// -// helma library stuff -/////////////////////////////////////////// +/*************************** + * HELMA LIBRARY STUFF * + ***************************/ + helma.lib = "Search"; helma.dontEnum(helma.lib);