I have the following issue using Lucene.NET 3.0.3.
My project analyze Documents using StandardAnalyzer with StopWord-List (combined german and english words).
While searching I create my searchterm by hand and parse it using MultiFieldQueryParser. The Parser is initialized with the same analyzer as indexing documents.
The parsed search query initialized a BooleanQuery. The BooleanQuery and a TopScoreDocCollector search in the Lucene index with IndexSearcher.
My code looks like:
using (StandardAnalyzer analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30, roxConnectionTools.getServiceInstance<ISearchIndexService>().GetStopWordList()))
{
...
MultiFieldQueryParser parser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, searchFields, analyzer);
parser.MultiTermRewriteMethod = MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE;
parser.AllowLeadingWildcard = true;
...
Query searchQuery = parser.Parse(searchStringBuilder.ToString().Trim);
...
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.Add(searchQuery, Occur.MUST);
...
TopScoreDocCollector scoreCollector = TopScoreDocCollector.Create(SearchServiceTools.MAX_SCORE_COLLECTOR_SIZE, true);
...
searcher.Search(boolQuery, scoreCollector);
ScoreDoc[] scoreDocs = scoreCollector.TopDocs().ScoreDocs;
}
If I index a document field with value "Test- und Produktivumgebung" I can´t find this document by searching this term.
I get results if I correct the search term to "Test- Produktivumgebung".
The word "und" is in my StopWord-List.
My search query looks like the following:
Manually generated search query: (+*Test* +*und* +*Produktivumgebung*)
Parsed search query: +(title:*Test*) +(title:*und*) +(title:*Produktivumgebung*)
Why I can´t find the document searching for "Test- und Produktivumgebung"?
Wildcard Queries are not analyzed (See this question, for an example). Since you are (if I understand correctly), interpreting the query "Test- und Produktivumgebung" to (+*Test* +*und* +*Produktivumgebung*), the analyzer is not used for any of those wildcard queries, and stop words will not be eliminated.
If you eliminate the step that performs that translation, the query "Test- und Produktivumgebung" should be parsed to a phrase query and analyzed, and should work just fine. Another reason to eliminate that step, is that applying a leading wildcard to every term will cause your performance to become very poor. That's why leading wildcards must be manually enabled, because it is generally a bad idea to use them.
Related
There are two queries,one is created by QueryParser:
QueryParser parser = new QueryParser(field, analyzer);
Query query1 = parser.parse("Lucene");
the other is term query:
Query query2=new TermQuery(new Term("title", "Lucene"));
what is the difference between query1 and query2?
This is the definition of Term from lucene docs.
A Term represents a word from text. This is the unit of search. It is composed of two elements, the text of the word, as a string, and the name of the field that the text occurred in.
So in your case the query will be created to search the word "Lucene" in the field "title".
To explain the difference between the two let me take a difference example,
consider the following
Query query2 = new TermQuery(new Term("title", "Apache Lucene"));
In this case the query will search for the exact word "Apache Lucene" in the field title.
In the other case
As an example, let's assume a Lucene index contains two fields, "title" and "body".
QueryParser parser = new QueryParser("title", "StandardAnalyzer");
Query query1 = parser.parse("title:Apache body:Lucene");
Query query2 = parser.parse("title:Apache Lucene");
Query query3 = parser.parse("title:\"Apache Lucene\"");
couple of things.
"title" is the field that QueryParser will search if you don't prefix it with a field.(as given in the constructor).
parser.parse("title:Apache body:Lucene"); -> in this case the final query will look like this. query2 = title:Apache body:Lucene.
parser.parse("body:Apache Lucene"); -> in this case the final query will also look like this. query2 = body:Apache title:Lucene. but for a different reason.
So the parser will search "Apache" in body field and "Lucene" in title field. Since The field is only valid for the term that it directly precedes,(http://lucene.apache.org/core/2_9_4/queryparsersyntax.html)
So since we do not specify any field for lucene , the default field which is "title" will be used.
query2 = parser.parse("title:\"Apache Lucene\""); in this case we are explicitly telling that we want to search for "Apache Lucene" in field "title". This is phrase query and is similar to Term query if analyzed correctly.
So to summarize the term query will not analyze the term and search as it is. while Query parser parses the input based on some conditions described above.
The QueryParser parses the string and constructs a BooleanQuery (afaik) consisting of BooleanClauses and analyzes the terms along the way.
The TermQuery does NOT do analysis, and takes the term as-is. This is the main difference.
So the query1 and query2 might be equivalent (in a sense, that they provide the same search results) if the field is the same, and the QueryParser's analyzer is not changing the term.
I have Lucene index which has city names.
Consider I want to search for 'New Delhi'. I have string 'New Del' which I want to pass to Lucene searcher and I am expecting output as 'New Delhi'.
If I generate query like Name:New Del* It will give me all cities with 'New and Del'in it.
Is there any way by which I can create Lucene query wildcard query with spaces in it?
I referred and tried few solutions given # http://www.gossamer-threads.com/lists/lucene/java-user/5487
It sounds like you have indexed your city names with analysis. That will tend to make this more difficult. With analysis, "new" and "delhi" are separate terms, and must be treated as such. Searching over multiple terms with wildcards like this tends to be a bit more difficult.
The easiest solution would be to index your city names without tokenization (lowercasing might not be a bad idea though). Then you would be able to search with the query parser simply by escaping the space:
QueryParser parser = new QueryParser("defaultField", analyzer);
Query query = parser.parse("cityname:new\\ del*");
Or you could use a simple WildcardQuery:
Query query = new WildcardQuery(new Term("cityname", "new del*"));
With the field analyzed by standard analyzer:
You will need to rely on SpanQueries, something like this:
SpanQuery queryPart1 = new SpanTermQuery(new Term("cityname", "new"));
SpanQuery queryPart2 = new SpanMultiTermQueryWrapper(new WildcardQuery(new Term("cityname", "del*")));
Query query = new SpanNearQuery(new SpanQuery[] {query1, query2}, 0, true);
Or, you can use the surround query parser (which provides query syntax intended to provide more robust support of span queries), using a query like W(new, del*):
org.apache.lucene.queryparser.surround.parser.QueryParser surroundparser = new org.apache.lucene.queryparser.surround.parser.QueryParser();
SrndQuery srndquery = surroundparser.parse("W(new, del*)");
query = srndquery.makeLuceneQueryField("cityname", new BasicQueryFactory());
As I learnt from the thread mentioned by you (http://www.gossamer-threads.com/lists/lucene/java-user/5487), you can either do an exact match with space or treat either parts w/ wild card.
So something like this should work - [New* Del*]
I serialized a BooleanQuery constructed using TermQuery's into a string. Now I am trying to de-serialize the string back into a BooleanQuery on a different node in a distributed system. So while de-serializing, I have multiple fields and I do not want to use an analyzer
Eg : I am trying to parse the below string without analyzing
+contents:maxItemsPerBlock +path:/lucene-5.1.0/core/src/java/org/apache/lucene/codecs/blocktree/Stats.java
QueryParser in lucene requires an analyzer, but I want the above field values to be treated as terms. I am looking for a query parser which does something like the below since I do not want to parse the strings and construct the query myself.
TermQuery q1 = new TermQuery(new Term("contents", "maxItemsPerBlock"));
TermQuery q2 = new TermQuery(new Term("path", "/lucene-5.1.0/core/src/java/org/apache/lucene/codecs/blocktree/Stats.java"));
BooleanQuery q = new BooleanQuery();
q.add(q1, BooleanClause.Occur.MUST);
q.add(q2, BooleanClause.Occur.MUST);
Also when I tried using a whitespace analyzer with a QueryParser, I got an "IllegalArgumentException : field must not be null" error. Below is the sample code
Analyzer analyzer = new WhitespaceAnalyzer();
String field = "contents";
QueryParser parser = new QueryParser(null, analyzer);
Query query = parser.parse("+contents:maxItemsPerBlock +path:/home/rchallapalli/Desktop/lucene-5.1.0/core/src/java/org/apache/lucene/codecs/blocktree/Stats.java");
java.lang.IllegalArgumentException: field must not be null
at org.apache.lucene.search.MultiTermQuery.<init>(MultiTermQuery.java:233)
at org.apache.lucene.search.AutomatonQuery.<init>(AutomatonQuery.java:99)
at org.apache.lucene.search.AutomatonQuery.<init>(AutomatonQuery.java:81)
at org.apache.lucene.search.RegexpQuery.<init>(RegexpQuery.java:108)
at org.apache.lucene.search.RegexpQuery.<init>(RegexpQuery.java:93)
at org.apache.lucene.queryparser.classic.QueryParserBase.newRegexpQuery(QueryParserBase.java:572)
at org.apache.lucene.queryparser.classic.QueryParserBase.getRegexpQuery(QueryParserBase.java:774)
at org.apache.lucene.queryparser.classic.QueryParserBase.handleBareTokenQuery(QueryParserBase.java:844)
at org.apache.lucene.queryparser.classic.QueryParser.Term(QueryParser.java:348)
at org.apache.lucene.queryparser.classic.QueryParser.Clause(QueryParser.java:247)
at org.apache.lucene.queryparser.classic.QueryParser.Query(QueryParser.java:202)
at org.apache.lucene.queryparser.classic.QueryParser.TopLevelQuery(QueryParser.java:160)
at org.apache.lucene.queryparser.classic.QueryParserBase.parse(QueryParserBase.java:117)
Considering the text you offer in your question. Maybe WhitespaceAnalyzer which splits tokens at whitespace is a choice.
Before you serialize the BooleanQuery constructed by TermQuery, the term in TermQuery is actually what you want to match in the Lucene Index.
// code in Scala
val parser = new QueryParser(version, "", new WhitespaceAnalyzer((version)))
val parsedQuery = parser.parse(searchString)
I tried the following two cases: single-value field and multi-valued field, all work.
+contents:maxItemsPerBlock +path:/lucene-5.1.0/core/src/java/org/apache/lucene/codecs/blocktree/Stats.java
+(contents:maxItemsPerBlock contents:minItemsPerBlock) +path:/lucene-5.1.0/core/src/java/org/apache/lucene/codecs/blocktree/Stats.java
Besides, in our system the serialization and deserialization when it comes to Query passing between nodes are based on
java's ObjectInputStream and ObjectOutputStream. So you may try in that way so you don't have to consider the Analyzer thing.
I recently updated my Hibernate Search to 5.0.0.Alpha4, which uses Lucene 4.8.1.
I still use the same codes to create my search query as before (I used Lucene 3.3 before updating, it was a really old version:)). But I noticed a problem, that the new version just ignores my operator and uses the default operator all the time, however the codes worked fine in the older version:
For Example: Now I set "AND" as default Operator. I just typed "java or php" in the search field. And I made a breakpoint at the line of queryParser.parse(searchString). It tells me that my searchString is now "java or php", which is correct. But the created searchQuery after queryParser.parse() is:
+(title:java) +(title:php)
Which means that Lucene deals my searchString as "AND" LOGIC!
I don't know if it is a bug of newer Lucene or just i did something wrong.
Here are the codes:
StandardAnalyzer analyzer = new StandardAnalyzer(
Version.LUCENE_47);
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(
Version.LUCENE_47,
mySearchFields,
analyzer);
queryParser.setAllowLeadingWildcard(true);
queryParser.setDefaultOperator(myDefaultOperator);
queryParser.setAutoGeneratePhraseQueries(true);
Query searchQuery = queryParser.parse(searchString);
FullTextQuery jpaQuery = getFullTextEntityManager()
.createFullTextQuery(searchQuery, entities);
jpaQuery.setMaxResults(ORACLE_MAXIMUM_ELEMENTS_IN_EXPRESSION);
Boolean Operators must be in CAPS. That is: java OR php is correct, java or php is not.
To explain exactly what is going on, without or being in caps, it's treated as another term. With AND being the default operator, this makes it:
java AND or AND php
or, something like
+(title:java) +(title:or) +(title:php)
However, or is a standard stop word, and so it will be eliminated during analysis of the query, and you are left with simply:
+(title:java) +(title:php)
I've got an index where I need to get all documents with a standard search, still ranked by relevance, even if a document isn't a hit.
My first idea is to add a field that is always matched, but that might deform the relevance score.
Use a BooleanQuery to combine your original query with a MatchAllDocsQuery. You can mitigate the effect this has on scoring by setting the boost on the MatchAllDocsQuery to zero before you combine it with your main query. This way you don't have to add an otherwise bogus field to the index.
For example:
// Parse a query by the user.
QueryParser qp = new QueryParser(Version.LUCENE_35, "text", new StandardAnalyzer());
Query standardQuery = qp.parse("User query may go here");
// Make a query that matches everything, but has no boost.
MatchAllDocsQuery matchAllDocsQuery = new MatchAllDocsQuery();
matchAllDocsQuery.setBoost(0f);
// Combine the queries.
BooleanQuery boolQuery = new BooleanQuery();
boolQuery.add(standardQuery, BooleanClause.Occur.SHOULD);
boolQuery.add(matchAllDocsQuery, BooleanClause.Occur.SHOULD);
// Now just pass it to the searcher.
This should give you hits from standardQuery followed by the rest of the documents in the index.