search.jsp revision 986
0N/A<%--
0N/ACDDL HEADER START
0N/A
0N/AThe contents of this file are subject to the terms of the
0N/ACommon Development and Distribution License (the "License").
0N/AYou may not use this file except in compliance with the License.
0N/A
0N/ASee LICENSE.txt included in this distribution for the specific
0N/Alanguage governing permissions and limitations under the License.
0N/A
0N/AWhen distributing Covered Code, include this CDDL HEADER in each
0N/Afile and include the License file at LICENSE.txt.
0N/AIf applicable, add the following below this CDDL HEADER, with the
0N/Afields enclosed by brackets "[]" replaced with your own identifying
0N/Ainformation: Portions Copyright [yyyy] [name of copyright owner]
0N/A
0N/ACDDL HEADER END
0N/A
928N/ACopyright 2010 Sun Microsystems, Inc. All rights reserved.
0N/AUse is subject to license terms.
0N/A
0N/A--%><%@ page import = "javax.servlet.*,
0N/Ajava.lang.Integer,
0N/Ajavax.servlet.http.*,
0N/Ajava.util.Hashtable,
0N/Ajava.util.Vector,
0N/Ajava.util.Date,
0N/Ajava.util.ArrayList,
58N/Ajava.util.List,
0N/Ajava.lang.*,
0N/Ajava.io.*,
0N/Ajava.io.StringReader,
0N/Aorg.opensolaris.opengrok.analysis.*,
207N/Aorg.opensolaris.opengrok.index.IndexDatabase,
0N/Aorg.opensolaris.opengrok.search.*,
0N/Aorg.opensolaris.opengrok.web.*,
0N/Aorg.opensolaris.opengrok.search.context.*,
58N/Aorg.opensolaris.opengrok.configuration.*,
99N/Aorg.apache.lucene.search.spell.LuceneDictionary,
99N/Aorg.apache.lucene.search.spell.SpellChecker,
928N/Aorg.apache.lucene.search.SortField,
928N/Aorg.apache.lucene.search.TopScoreDocCollector,
99N/Aorg.apache.lucene.store.FSDirectory,
0N/Aorg.apache.lucene.analysis.*,
0N/Aorg.apache.lucene.document.*,
0N/Aorg.apache.lucene.index.*,
0N/Aorg.apache.lucene.search.*,
0N/Aorg.apache.lucene.queryParser.*"
0N/A%><%@ page session="false" %><%@ page errorPage="error.jsp" %><%
0N/ADate starttime = new Date();
0N/AString q = request.getParameter("q");
0N/AString defs = request.getParameter("defs");
0N/AString refs = request.getParameter("refs");
0N/AString hist = request.getParameter("hist");
0N/AString path = request.getParameter("path");
58N/A
207N/A%><%@ include file="projects.jspf" %><%
98N/AString sort = null;
98N/A
98N/Afinal String LASTMODTIME = "lastmodtime";
98N/Afinal String RELEVANCY = "relevancy";
819N/Afinal String BY_PATH = "fullpath";
928N/Afinal SortField S_BY_PATH = new SortField(BY_PATH,SortField.STRING);
98N/A
98N/ACookie[] cookies = request.getCookies();
98N/Aif (cookies != null) {
98N/A for (Cookie cookie : cookies) {
98N/A if (cookie.getName().equals("OpenGrok/sorting")) {
98N/A sort = cookie.getValue();
819N/A if (!LASTMODTIME.equals(sort) && !RELEVANCY.equals(sort) && !BY_PATH.equals(sort)) {
830N/A sort = RELEVANCY;
98N/A }
98N/A break;
98N/A }
98N/A }
98N/A}
98N/A
98N/AString sortParam = request.getParameter("sort");
98N/Aif (sortParam != null) {
98N/A if (LASTMODTIME.equals(sortParam)) {
98N/A sort = LASTMODTIME;
98N/A } else if (RELEVANCY.equals(sortParam)) {
98N/A sort = RELEVANCY;
819N/A } else if (BY_PATH.equals(sortParam)) {
819N/A sort = BY_PATH;
98N/A }
98N/A if (sort != null) {
98N/A Cookie cookie = new Cookie("OpenGrok/sorting", sort);
98N/A response.addCookie(cookie);
98N/A }
830N/A} else { sort = RELEVANCY; }
58N/A
830N/A//List<org.apache.lucene.document.Document> docs=new ArrayList<org.apache.lucene.document.Document>();
0N/AString errorMsg = null;
0N/A
0N/Aif( q!= null && q.equals("")) q = null;
0N/Aif( defs != null && defs.equals("")) defs = null;
0N/Aif( refs != null && refs.equals("")) refs = null;
0N/Aif( hist != null && hist.equals("")) hist = null;
0N/Aif( path != null && path.equals("")) path = null;
830N/Aif (project != null && project.size()<1) project = null;
0N/A
0N/Aif (q != null || defs != null || refs != null || hist != null || path != null) {
0N/A Searcher searcher = null; //the searcher used to open/search the index
928N/A TopScoreDocCollector collector=null; // the collector used
819N/A ScoreDoc[] hits = null; // list of documents which result from the query
0N/A IndexReader ireader = null; //the reader used to open/search the index
986N/A Query query = null; //the Query created by the QueryBuilder
819N/A boolean allCollected=false;
819N/A int totalHits=0;
0N/A
0N/A int start = 0; //the first index displayed on this page
830N/A //TODO deprecate max this and merge with paging and param n - TEST needed
830N/A //int max = 25; //the maximum items displayed on this page
830N/A int max=RuntimeEnvironment.getInstance().getHitsPerPage();
819N/A
819N/A int hitsPerPage = RuntimeEnvironment.getInstance().getHitsPerPage();
819N/A int cachePages= RuntimeEnvironment.getInstance().getCachePages();
928N/A final boolean docsScoredInOrder=false;
819N/A
0N/A int thispage = 0; //used for the for/next either max or
986N/A
986N/A QueryBuilder queryBuilder =
986N/A new QueryBuilder()
986N/A .setFreetext(q).setDefs(defs).setRefs(refs)
986N/A .setPath(path).setHist(hist);
986N/A
0N/A try {
58N/A String DATA_ROOT = env.getDataRootPath();
0N/A if(DATA_ROOT.equals("")) {
0N/A throw new Exception("DATA_ROOT parameter is not configured in web.xml!");
0N/A }
0N/A File data_root = new File(DATA_ROOT);
0N/A if(!data_root.isDirectory()) {
0N/A throw new Exception("DATA_ROOT parameter in web.xml does not exist or is not a directory!");
0N/A }
0N/A //String date = request.getParameter("date");
0N/A try {
819N/A //TODO merge paging hitsPerPage with parameter n (has to reflect the search if changed so proper number is cached first time)
0N/A start = Integer.parseInt(request.getParameter("start")); //parse the max results first
0N/A max = Integer.parseInt(request.getParameter("n")); //then the start index
0N/A if(max < 0 || (max % 10 != 0) || max > 50) max = 25;
0N/A if(start < 0 ) start = 0;
0N/A } catch (Exception e) { }
947N/A
986N/A query = queryBuilder.build();
830N/A
207N/A File root = new File(RuntimeEnvironment.getInstance().getDataRootFile(),
207N/A "index");
207N/A
207N/A if (RuntimeEnvironment.getInstance().hasProjects()) {
207N/A if (project == null) {
207N/A errorMsg = "<b>Error:</b> You must select a project!";
207N/A } else {
830N/A if (project.size() > 1) { //more projects
830N/A IndexSearcher[] searchables = new IndexSearcher[project.size()];
830N/A File droot = new File(RuntimeEnvironment.getInstance().getDataRootFile(), "index");
830N/A int ii = 0;
830N/A //TODO might need to rewrite to Project instead of String , need changes in projects.jspf too
830N/A for (String proj : project) {
928N/A ireader = (IndexReader.open(FSDirectory.open(new File(droot, proj)),true));
830N/A searchables[ii++] = new IndexSearcher(ireader);
830N/A }
830N/A if (Runtime.getRuntime().availableProcessors() > 1) {
830N/A searcher = new ParallelMultiSearcher(searchables);
830N/A } else {
830N/A searcher = new MultiSearcher(searchables);
830N/A }
830N/A } else { // just 1 project selected
830N/A root = new File(root, project.get(0));
928N/A ireader = IndexReader.open(FSDirectory.open(root),true);
830N/A searcher = new IndexSearcher(ireader);
830N/A }
207N/A }
830N/A } else { //no project setup
928N/A ireader = IndexReader.open(FSDirectory.open(root),true);
830N/A searcher = new IndexSearcher(ireader);
830N/A }
207N/A
830N/A //TODO check if below is somehow reusing sessions so we don't requery again and again, I guess 2min timeout sessions could be usefull, since you click on the next page within 2mins, if not, then wait ;)
830N/A if (errorMsg == null) {
928N/A collector = TopScoreDocCollector.create(hitsPerPage*cachePages,docsScoredInOrder);
819N/A if (LASTMODTIME.equals(sort)) {
928N/A Sort sortf = new Sort(new SortField("date",SortField.STRING,true));
819N/A TopFieldDocs fdocs=searcher.search(query, null,hitsPerPage*cachePages, sortf);
819N/A totalHits=fdocs.totalHits;
819N/A if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
819N/A fdocs=searcher.search(query, null, totalHits, sortf);
819N/A allCollected=true;
819N/A }
819N/A hits = fdocs.scoreDocs;
819N/A } else if (BY_PATH.equals(sort)) {
928N/A Sort sortf = new Sort(S_BY_PATH);
819N/A TopFieldDocs fdocs=searcher.search(query, null,hitsPerPage*cachePages, sortf);
819N/A totalHits=fdocs.totalHits;
819N/A if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
819N/A fdocs=searcher.search(query, null,totalHits, sortf);
819N/A allCollected=true;
819N/A }
819N/A hits = fdocs.scoreDocs;
819N/A } else {
819N/A searcher.search(query,collector);
819N/A totalHits=collector.getTotalHits();
819N/A if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
928N/A collector = TopScoreDocCollector.create(totalHits,docsScoredInOrder);
819N/A searcher.search(query,collector);
819N/A allCollected=true;
819N/A }
819N/A hits=collector.topDocs().scoreDocs;
819N/A }
207N/A
819N/A //below will get all the documents
819N/A// for (int i = 0; i < hits.length; i++) {
819N/A// int docId = hits[i].doc;
819N/A// Document d = searcher.doc(docId);
819N/A// docs.add(d);
819N/A// }
819N/A
207N/A }
0N/A thispage = max;
0N/A } catch (BooleanQuery.TooManyClauses e) {
0N/A errorMsg = "<b>Error:</b> Too many results for wildcard!";
986N/A } catch (ParseException e) {
986N/A errorMsg = "<b>Error parsing your query</b>" +
986N/A "<p/>You might try to enclose your search term in quotes, " +
986N/A "<a href=\"help.jsp#escaping\">escape special characters</a> " +
986N/A "with <b>\\</b>, or read the <a href=\"help.jsp\">Help</a> " +
986N/A "on the query language.<p/>" +
510N/A "Error message from parser:<br/>" + Util.htmlize(e.getMessage());
0N/A } catch (FileNotFoundException e) {
0N/A errorMsg = "<b>Error:</b> Index database not found";
0N/A } catch (Exception e) {
345N/A errorMsg = "<b>Error:</b> " + Util.htmlize(e.getMessage());
0N/A }
850N/A
942N/A // Bug #3900: Check if this is a search for a single term, and that term
942N/A // is a definition. If that's the case, and we only have one match, we'll
942N/A // generate a direct link instead of a listing.
942N/A boolean isSingleDefinitionSearch =
942N/A (query instanceof TermQuery) && (defs != null);
942N/A
946N/A // Attempt to create a direct link to the definition if we search for one
946N/A // single definition term AND we have exactly one match AND there is only
946N/A // one definition of that symbol in the document that matches.
946N/A boolean uniqueDefinition = false;
946N/A if (isSingleDefinitionSearch && hits != null && hits.length == 1) {
946N/A Document doc = searcher.doc(hits[0].doc);
946N/A byte[] rawTags = doc.getFieldable("tags").getBinaryValue();
946N/A Definitions tags = Definitions.deserialize(rawTags);
946N/A String symbol = ((TermQuery) query).getTerm().text();
946N/A if (tags.occurrences(symbol) == 1) {
946N/A uniqueDefinition = true;
946N/A }
946N/A }
946N/A
107N/A // @TODO fix me. I should try to figure out where the exact hit is instead
107N/A // of returning a page with just _one_ entry in....
946N/A if (uniqueDefinition && request.getServletPath().equals("/s")) {
819N/A String preFragmentPath = Util.URIEncodePath(context + "/xref" + searcher.doc(hits[0].doc).get("path"));
158N/A String fragment = Util.URIEncode(((TermQuery)query).getTerm().text());
151N/A
158N/A StringBuilder url = new StringBuilder(preFragmentPath);
152N/A url.append("#");
158N/A url.append(fragment);
152N/A
152N/A response.sendRedirect(url.toString());
0N/A } else {
144N/A String pageTitle = "Search";
147N/A RuntimeEnvironment environment = RuntimeEnvironment.getInstance();
147N/A environment.register();
144N/A %><%@ include file="httpheader.jspf" %>
0N/A<body>
0N/A<div id="page">
144N/A <div id="header"><%@ include file="pageheader.jspf" %></div>
0N/A<div id="Masthead"></div>
0N/A<div id="bar">
850N/A <table border="0" width="100%"><tr><td><a href="<%=context%>" id="home">Home</a></td><td align="right"><%
98N/A {
853N/A String url = "search?";
850N/A url = url + (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
850N/A (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
850N/A (refs == null ? "" : "&amp;refs=" + Util.URIEncode(refs)) +
850N/A (path == null ? "" : "&amp;path=" + Util.URIEncode(path)) +
850N/A (hist == null ? "" : "&amp;hist=" + Util.URIEncode(hist));
850N/A if (hasProjects) {
866N/A if (project!=null) {
866N/A url = url + "&amp;project=";
866N/A for (Iterator it = project.iterator(); it.hasNext();) {
866N/A url = url + (project == null ? "" : Util.URIEncode((String) it.next()) + ",");
866N/A }
850N/A }
98N/A }
850N/A
850N/A %>Sort by: <%
850N/A url=url+("&amp;sort=");
98N/A
98N/A if (sort == null || RELEVANCY.equals(sort)) {
850N/A %><b>relevance</b> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
819N/A } else if (LASTMODTIME.equals(sort)) {
850N/A %><a href="<%=url+RELEVANCY%>">relevance</a> | <b>last modified time</b> | <a href="<%=url+BY_PATH%>">path</a><%
819N/A } else if (BY_PATH.equals(sort)) {
850N/A %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <b>path</b><%
98N/A } else {
850N/A %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
98N/A }
98N/A } %></td></tr></table>
98N/A</div>
98N/A<div id="menu">
98N/A <%@ include file="menu.jspf"%>
0N/A</div>
0N/A<div id="results">
0N/A<%
830N/A//TODO spellchecking cycle below is not that great and we only create suggest links for every token in query, not for a query as whole
0N/Aif( hits == null || errorMsg != null) {
0N/A %><%=errorMsg%><%
819N/A } else if (hits.length == 0) {
99N/A File spellIndex = new File(env.getDataRootPath(), "spellIndex");
830N/A File[] spellIndexes=null;
207N/A
207N/A if (RuntimeEnvironment.getInstance().hasProjects()) {
830N/A if (project.size() > 1) { //more projects
830N/A spellIndexes = new File[project.size()];
830N/A int ii = 0;
830N/A //TODO might need to rewrite to Project instead of String , need changes in projects.jspf too
830N/A for (String proj : project) {
830N/A spellIndexes[ii++] = new File(spellIndex,proj);
830N/A }
830N/A } else { // just 1 project selected
830N/A spellIndex = new File(spellIndex, project.get(0));
830N/A }
207N/A }
830N/A
830N/A int count=1;
830N/A if (spellIndexes!=null) {count=spellIndexes.length;}
830N/A
830N/A for (int idx = 0; idx < count; idx++) {
830N/A
830N/A if (spellIndexes!=null) spellIndex = spellIndexes[idx];
830N/A
830N/A if (spellIndex.exists()) {
928N/A FSDirectory spellDirectory = FSDirectory.open(spellIndex);
99N/A SpellChecker checker = new SpellChecker(spellDirectory);
99N/A
0N/A Date sstart = new Date();
603N/A boolean printHeader = true;
0N/A String[] toks;
0N/A if(q != null) {
0N/A toks = q.split("[\t ]+");
0N/A if(toks != null){
0N/A for(int j=0; j<toks.length; j++) {
0N/A if(toks[j].length() > 3) {
830N/A String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
0N/A for(int i = 0;i < ret.length; i++) {
603N/A if (printHeader) {
830N/A %><p><font color="#cc0000">Did you mean(for <%=spellIndex.getName()%>)</font>:<%
603N/A printHeader = false;
603N/A }
603N/A %> <a href=search?q=<%=ret[i]%>><%=ret[i]%></a> &nbsp; <%
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A if(refs != null) {
0N/A toks = refs.split("[\t ]+");
0N/A if(toks != null){
0N/A for(int j=0; j<toks.length; j++) {
0N/A if(toks[j].length() > 3) {
830N/A String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
0N/A for(int i = 0;i < ret.length; i++) {
603N/A if (printHeader) {
830N/A %><p><font color="#cc0000">Did you mean(for <%=spellIndex.getName()%>)</font>:<%
603N/A printHeader = false;
603N/A }
830N/A %> <a href=search?refs=<%=ret[i]%>><%=ret[i]%></a> &nbsp; <%
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
830N/A //TODO it seems the only true spellchecker is for below field, see IndexDatabase createspellingsuggestions ...
0N/A if(defs != null) {
0N/A toks = defs.split("[\t ]+");
0N/A if(toks != null){
0N/A for(int j=0; j<toks.length; j++) {
0N/A if(toks[j].length() > 3) {
830N/A String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
0N/A for(int i = 0;i < ret.length; i++) {
603N/A if (printHeader) {
830N/A %><p><font color="#cc0000">Did you mean(for <%=spellIndex.getName()%>)</font>:<%
603N/A printHeader = false;
603N/A }
830N/A %> <a href=search?defs=<%=ret[i]%>><%=ret[i]%></a> &nbsp; <%
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
603N/A if (printHeader) {
603N/A %></p><%
603N/A }
99N/A spellDirectory.close();
830N/A }
830N/A
0N/A }
830N/A
0N/A %><p> Your search <b><%=query.toString()%></b> did not match any files.
0N/A <br />
0N/A Suggestions:<br/><blockquote>- Make sure all terms are spelled correctly.<br/>
0N/A - Try different keywords.<br/>
0N/A - Try more general keywords.<br/>
0N/A - Use 'wil*' cards if you are looking for partial match.
0N/A </blockquote>
0N/A </p><%
0N/A } else { // We have a lots of results to show
0N/A StringBuilder slider = null;
820N/A if ( max < totalHits) {
820N/A if((start + max) < totalHits) {
0N/A thispage = max;
0N/A } else {
820N/A thispage = totalHits - start;
0N/A }
850N/A String urlp = (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
0N/A (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
0N/A (refs == null ? "" : "&amp;refs=" + Util.URIEncode(refs)) +
0N/A (path == null ? "" : "&amp;path=" + Util.URIEncode(path)) +
97N/A (hist == null ? "" : "&amp;hist=" + Util.URIEncode(hist)) +
830N/A (sort == null ? "" : "&amp;sort=" + Util.URIEncode(sort));
850N/A if (hasProjects) {
850N/A urlp = urlp + "&amp;project=";
850N/A for (Iterator it = project.iterator(); it.hasNext();) {
850N/A urlp = urlp + (project == null ? "" : Util.URIEncode((String) it.next()) + ",");
850N/A }
850N/A }
0N/A slider = new StringBuilder();
0N/A int labelStart =1;
0N/A int sstart = start - max* (start / max % 10 + 1) ;
0N/A if(sstart < 0) {
0N/A sstart = 0;
0N/A labelStart = 1;
0N/A } else {
0N/A labelStart = sstart/max + 1;
0N/A }
0N/A int label = labelStart;
0N/A int labelEnd = label + 11;
0N/A String arr;
819N/A for(int i=sstart; i<totalHits && label <= labelEnd; i+= max) {
0N/A if (i <= start && start < i+ max) {
0N/A slider.append("<span class=\"sel\">" + label + "</span>");
0N/A } else {
0N/A if(label == labelStart && label != 1) {
0N/A arr = "&lt;&lt";
819N/A } else if(label == labelEnd && i < totalHits) {
0N/A arr = "&gt;&gt;";
0N/A } else {
0N/A arr = label < 10 ? " " + label : String.valueOf(label);
0N/A }
850N/A slider.append("<a class=\"more\" href=\"s?n=" + max + "&amp;start=" + i + urlp + "\">"+
0N/A arr + "</a>");
0N/A }
0N/A label++;
0N/A }
0N/A } else {
819N/A thispage = totalHits - start; // set the max index to max or last
0N/A }
0N/A %>&nbsp; &nbsp; Searched <b><%=query.toString()%></b> (Results <b><%=start+1%> -
819N/A <%=thispage+start%></b> of <b><%=totalHits%></b>) sorted by <%=sort%> <p><%=slider != null ?
0N/A slider.toString(): ""%></p>
0N/A <table width="100%" cellpadding="3" cellspacing="0" border="0"><%
0N/A
0N/A Context sourceContext = null;
0N/A Summarizer summer = null;
0N/A if (query != null) {
0N/A try{
986N/A sourceContext =
986N/A new Context(query, queryBuilder.getQueries());
0N/A if(sourceContext != null)
985N/A summer = new Summarizer(query,
986N/A new CompatibleAnalyser());
0N/A } catch (Exception e) {
0N/A
0N/A }
0N/A }
0N/A
0N/A HistoryContext historyContext = null;
0N/A try {
0N/A historyContext = new HistoryContext(query);
0N/A } catch (Exception e) {
0N/A }
0N/A EftarFileReader ef = null;
0N/A try{
58N/A ef = new EftarFileReader(env.getDataRootPath() + "/index/dtags.eftar");
0N/A } catch (Exception e) {
0N/A }
830N/A //TODO also fix the way what and how it is passed to prettyprint, can improve performance! SearchEngine integration is really needed here.
819N/A Results.prettyPrintHTML(searcher,hits, start, start+thispage,
0N/A out,
0N/A sourceContext, historyContext, summer,
0N/A context + "/xref",
0N/A context + "/more",
58N/A env.getSourceRootPath(),
58N/A env.getDataRootPath(),
0N/A ef);
0N/A if(ef != null) {
0N/A try{
0N/A ef.close();
0N/A } catch (IOException e) {
0N/A }
0N/A }
0N/A %></table><br/>
0N/A <b> Completed in <%=(new Date()).getTime() - starttime.getTime()%> milliseconds </b> <br/>
0N/A <%=slider != null ? "<p>" + slider + "</p>" : ""%>
0N/A <%
0N/A }
66N/A %><br/></div><%@include file="foot.jspf"%><%
0N/A }
0N/A if (ireader != null)
0N/A ireader.close();
0N/A} else { // Entry page show the map
58N/A response.sendRedirect(context + "/index.jsp");
0N/A}
0N/A%>