Complete revamp of luceneQueryConstructor.

Added demo and introduction HTML page.
Added jsUnit tests.

WARNING: this version should be considered incompatible with previous versions.
Migration effort is minimal and I'm happy to answer questions about migration posted to lucene-user, but
I'm under the impression the previous versions weren't widely used (if at all) anyways.


git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@150979 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Kelvin Tan 2004-05-17 13:04:42 +00:00
parent aa7e95dae2
commit bc26b64269
3 changed files with 477 additions and 134 deletions

View File

@ -1,68 +1,185 @@
<html>
<head>
<title>Demo Lucene Query Constructor</title>
<meta name="Author" content="Kelvin Tan">
<title>Lucene Query Constructor Demo and Introduction</title>
<script type="text/javascript" src="luceneQueryConstructor.js"></script>
<script type="text/javascript" src="../queryValidator/luceneQueryValidator.js"></script>
<script>
function submitForm(frm)
{
if(frm["noField-phrase-input"].value.length > 0)
frm["noField-phrase"].value = quote(frm["noField-phrase-input"].value)
else if(frm["noField-phrase"].value.length > 0)
frm["noField-phrase"].value = ''
doMakeQuery(frm.query,true);
}
</script>
</head>
<body>
<h2>Lucene Javascript Query Constructor</h2>
<p>luceneQueryConstructor.js is a Javascript framework for constructing queries using the "advanced" search features of lucene,
namely field-searching, boolean searching, phrase searching, group searching (via parentheses) and various combinations of the aforementioned.
<p>It also provides a convenient way to mimic a Google-like search, where all terms are ANDed, as opposed to Lucene's default OR modifier.
<p>
This HTML form provides examples on the usage of luceneQueryConstructor.js.
An interface similar to <a href="http://www.google.com/advanced_search">Google's Advanced Search</a> form is shown here.
<form>
<p>
<table width="100%" border="0" cellspacing="1" cellpadding="5">
<tr>
<th></th>
<td width="25%"></td>
<tr>
<th>
<input name="noField-andModifier" value="+|0" type="hidden"><b>Find results</b>
</th>
<td class="bodytext">With <b>all</b> of the words</td>
<td class="bodytext">
<input type="text" name="noField-and" size="25">
<select name="resultsPerPage">
<option value="10">10 results<option value="20">20 results
<option value="50" selected>50 results</select>
</td>
</tr>
<tr>
<th>
<input name="noField-phraseModifier" value="+|+" type="hidden">
</th>
<td class="bodytext">With the <b>exact phrase</b></td>
<td class="bodytext">
<input type="text" name="noField-phrase-input" size="25">
<input type="hidden" name="noField-phrase">
</td>
</tr>
<tr>
<th>
<input name="noField-orModifier" value=" |+" type="hidden">
</th>
<td class="bodytext">With <b>at least</b> one of the words</td>
<td class="bodytext">
<input type="text" name="noField-or" size="25">
</td>
</tr>
<tr>
<th>
<input name="noField-notModifier" value="-|0" type="hidden">
</th>
<td class="bodytext"><b>Without</b> the words</td>
<td class="bodytext">
<input type="text" name="noField-not" size="25">
</td>
</tr>
<tr>
<th>
<b>File Format</b>
</th>
<td class="bodytext">
<select name="fileNameModifier"><option value="And" selected>Only</option><option value="Not">Don't</option></select>
return results of the file format</td>
<td class="bodytext">
<select name="fileName"><option value="" selected>any format<option value="pdf">Adobe Acrobat PDF (.pdf)
<option value="doc">Microsoft Word (.doc)<option value="xls">Microsoft Excel (.xls)<option value="ppt">Microsoft Powerpoint (.ppt)</select>
</td>
</tr>
<tr>
<th>
<b>Date</b>
</th>
<td class="bodytext">
Return results updated in the </td>
<td class="bodytext">
<select name="date"><option value="" selected>anytime<option value="3">past 3 months
<option value="6">past 6 months<option value="12">past year</select>
<input type="hidden" name="fromDate">
<input type="hidden" name="toDate">
<input type="hidden" name="dateRangeField" value="lastModifiedDate">
</td>
</tr>
<input type="hidden" name="query">
Name:
<input type="text" name="name">
<select name="nameModifier">
<option value="And">And</option>
<option value="Or" selected="selected">Or</option>
<option value="Not">Not</option>
</select>
</p>
<p>
Description:
<input type="text" name="description">
<select name="descriptionModifier">
<option value="And">And</option>
<option value="Or" selected="selected">Or</option>
<option value="Not">Not</option>
</select>
</p>
<p>
Required Select:
<select name="choice">
<option value="one">One</option>
<option value="two">Two</option>
</select>
<input type="hidden" name="choiceModifier" value="And"/>
</p>
<p>
Multi-select:
<select name="multi" multiple="true" size="5">
<option value="multi1">multi1</option>
<option value="multi2">multi2</option>
<option value="multi3">multi3</option>
<option value="multi4">multi4</option>
</select>
<input type="hidden" name="multiModifier" value="Or"/>
</p>
<p>
Radio:
<!-- TODO: something fishy happens if none selected -->
<input type="radio" name="radio" value="wnrn" checked="checked" />WNRN
<input type="radio" name="radio" value="klpx" />KLPX
<input type="hidden" name="radioModifier" value="And"/>
</p>
<p>
Current Query:
<pre id="curQuery"></pre>
</p>
<input type="button" name="Update" value="Update Query" onClick="doMakeQuery(this.form.query); curQuery.innerText = this.form.query.value" />
<input type="button" name="Validate" value="Validate" onClick="doCheckLuceneQuery(this.form.query);" />
<tr><th></th><td></td><td><input type="button" value="Search" onClick="submitForm(this.form)"></td>
</table>
</form>
<p>luceneQueryConstructor works by assuming a certain naming convention of form fields to obtain the necessary information to construct the query.<br>
<b>NB:</b>Unless otherwise specified, all uses of the word <code>field</code> should be assumed to mean form input fields and not Lucene document fields.
<p>The input form field is expected to be the <b>same name as the Lucene Field</b>. For example, if you have a Document with <i>fileName</i> as a Field, and
you'd like to provide field-searching on this field, then introduce a form field like so:
<center>
<p><code>&lt;input type="text" name="fileName"&gt;</code>
</p>
</center>
You are also expected to provide another field known as
this field's <b>modifier</b>. This modifier field tells luceneQueryConstructor how to convert the field and value into a Lucene query. The naming convention
of the modifier is <b>&lt;name of input field/Lucene field&gt;&lt;modifier suffix as declared in luceneQueryConstructor.js&gt;</b>. So, for the <i>fileName</i> field
we introduced above, it's modifier field would be:
<center>
<p><code>&lt;input type="hidden" name="fileNameModifier" value="+|+"&gt;</code>
</p>
</center>
<p>The value of the modifier field is in the form <b>&lt;term modifier&gt;|&lt;group modifier&gt;</b>. Let me explain.
<p>Looking at the form above, we see fields that provide
<ol>
<li>AND search
<li>OR search
<li>NOT search
<li>and others which are unrelated to this discussion
</ol>
Given a value of <b><i>foo bar</i></b>, the AND search field must be converted to <b><i>+foo +bar</i></b> (luceneQueryConstructor only supports
using +/-, not AND/OR/NOT), the NOT search to <b><i>-foo -bar</i></b> and the OR search not at all.
<p>However, also consider the relationship <b>between</b> these groups of fields. Assuming Google's Advanced Search interface,
we're effectively saying that we want all of the terms in the AND search field <b>AND</b> at least one of the
terms in the OR search field <b>AND</b> none of the terms in the NOT search.
<p>So, if the AND, OR and NOT search fields all have the values of <b><i>foo bar</i></b>, then an appropriate search query
which fulfills the requirements would be
<center>
<p><code>+foo +bar +(foo bar) -foo -bar</code>
</p>
</center>
Well, to be more correct, it should be
<center>
<p><code>+(+foo +bar) +(foo bar) -foo -bar</code>
</p>
</center>
Hmmmm...if you're sharp, you would have noticed that the NOT terms aren't grouped.
You'll find that if you group them with an AND modifier, no results will be returned at all (though it's a valid query),
because the query constructed wouldn't make any sense at all. Lucene also implicitly ANDs NOT terms, it seems. In any case,
both queries as presented are correct, though I prefer the first one because it is less verbose.
<p>The following matrix provides modifiers and their effects on queries:<br><br>
<table border="1" align="center">
<tr>
<th>Boolean modifier
<th>Form value
<th>As term modifier
<th>As group modifier
</tr>
<tr align="center">
<td>AND
<td>+
<td>+term1 + term2 ...
<td>+(...)
</tr>
<tr align="center">
<td>OR
<td>(single space)
<td>term1 term2 ...
<td>(...)
</tr>
<tr align="center">
<td>NOT
<td>-
<td>-term1 -term2 ...
<td>-(...)
</tr>
<tr align="center">
<td>no modifier
<td>0
<td>term1 term2 ...
<td>no grouping
</tr>
</table>
<p>With this knowledge, we know that the value of the AND field modifier needs to be <b><i>+|0</i></b> for the first query and
<b><i>+|+</i></b> for the second query, the values of the NOT field modifier and the
OR field modifier are <b><i>-|0</i></b> and <b><i>&nbsp;|+</i></b> (it's an empty space before the |) in both queries respectively.
<p>
Well, that's all I have to say for now. There are more topics to be covered, such as construction of phrase searches, non-field searches,
multiple list box selections, radio buttons etc, but right now I'm not even sure if anyone will read this much! :-) Anyway, there's always the code.
</body>
</html>

View File

@ -3,14 +3,21 @@
// Version: $Id$
// Change this according to what you use to name the field modifiers in your form.
// e.g. with the field "name", the modifier will be called "nameModifier"
// e.g. with the search field "name", the form element of its modifier
// will be "name<modifierSuffix>"
var modifierSuffix = 'Modifier';
// If not a field-specific search
// e.g. with the arbitary form element 'foobar', its modifier will be
// <noFieldPrefix>foobarModifier and its form element
// will be <noFieldPrefix>foobar
var noFieldPrefix = 'noField-';
// Do you wish the query to be displayed as an alert box?
var debug = false;
// Do you wish the function to submit the form upon query construction?
var submitOnConstruction = false;
var submitForm = false;
// prefix modifier for boolean AND queries
var AND_MODIFIER = '+';
@ -19,10 +26,15 @@ var AND_MODIFIER = '+';
var NOT_MODIFIER = '-';
// prefix modifier for boolean OR queries
var OR_MODIFIER = '';
var OR_MODIFIER = ' ';
// default prefix modifier for boolean queries
var DEFAULT_MODIFIER = OR_MODIFIER;
var NO_MODIFIER = 0;
// default modifier for terms
var DEFAULT_TERM_MODIFIER = AND_MODIFIER;
// default modifier for groups of terms (denoted by parantheses)
var DEFAULT_GROUP_MODIFIER = AND_MODIFIER;
// used to delimit multiple values from checkboxes and select lists
var VALUE_DELIMITER = ' ';
@ -30,19 +42,18 @@ var VALUE_DELIMITER = ' ';
// Constructs the query
// @param query Form field to represent the constructed query to be submitted
// @param debug Turn on debugging?
// @return Submits the form if submitOnConstruction=true, else returns the query param
function doMakeQuery( query, dbg )
{
if(typeof(dbg) != "undefined")
debug = dbg;
var frm = query.form;
var formElements = frm.elements;
query.value = '';
// keep track of the fields we've examined
var dict = new Array();
for(var i=0; i<formElements.length; i++)
{
var element = formElements[i];
@ -50,45 +61,40 @@ function doMakeQuery( query, dbg )
if(elementName != "" && !contains(dict, elementName))
{
dict[dict.length] = elementName;
// ensure we get the whole group (of checkboxes, radio, etc), if applicable
var elementValue = trim(getFieldValue(frm[element.name]));
if(elementValue.length > 0 && elementValue != ' ')
var elementValue = getFieldValue(frm[element.name]);
if(elementValue.length > 0)
{
var subElement = frm[elementName + modifierSuffix];
if(typeof(subElement) != "undefined") // found a field/fieldModifier pair
{
// assume that the user only allows one logic, i.e. AND OR, AND NOT, OR NOT, etc not supported
var logic = getFieldValue(subElement);
if(logic == 'And')
var termMod, groupMod;
var modifier = getFieldValue(subElement);
// modifier field is in the form <termModifier>|<groupModifier>
if(modifier.indexOf('|') > -1)
{
addFieldWithModifier(query, AND_MODIFIER, elementName, elementValue);
}
else if(logic == 'Not')
{
addFieldWithModifier(query, NOT_MODIFIER, elementName, elementValue);
}
else if(logic == 'Or')
{
addFieldWithModifier(query, OR_MODIFIER, elementName, elementValue);
var idx = modifier.indexOf('|');
termMod = modifier.substring(0, idx);
if(termMod == '') termMod = DEFAULT_TERM_MODIFIER;
groupMod = modifier.substring(idx + 1);
if(groupMod == '') groupMod = DEFAULT_GROUP_MODIFIER;
}
else
{
addFieldWithModifier(query, DEFAULT_MODIFIER, elementName, elementValue);
termMod = modifier;
if(termMod == '') termMod = DEFAULT_TERM_MODIFIER;
groupMod = DEFAULT_GROUP_MODIFIER;
}
appendTerms(query, termMod, elementValue, elementName, groupMod);
}
}
}
}
if(debug)
{
alert('Query:' + query.value);
}
if(submitOnConstruction)
if(debug) {alert('Query:' + query.value);}
if(submitForm)
{
frm.submit();
}
@ -103,14 +109,8 @@ function doMakeQuery( query, dbg )
// @return Submits the form if submitOnConstruction=true, else returns the query param
function doANDTerms(query)
{
var temp = '';
splitStr = query.value.split(" ");
query.value = '';
for(var i=0;i<splitStr.length;i++)
{
if(splitStr[i].length > 0) addModifier(query, AND_MODIFIER, splitStr[i]);
}
if(submitOnConstruction)
appendTerms(query, AND_MODIFIER, query.value);
if(submitForm)
{
frm.submit();
}
@ -120,26 +120,103 @@ function doANDTerms(query)
}
}
function contains(array, s)
function buildTerms(termModifier, fieldValue)
{
for(var i=0; i<array.length; i++)
fieldValue = trim(fieldValue);
var splitStr = fieldValue.split(" ");
fieldValue = '';
var inQuotes = false;
for(var i=0;i<splitStr.length;i++)
{
if(s == array[i])
return true;
if(splitStr[i].length > 0)
{
if(!inQuotes)
{
fieldValue = fieldValue + termModifier + splitStr[i] + ' ';
}
else
{
fieldValue = fieldValue + splitStr[i] + ' ';
}
if(splitStr[i].indexOf('"') > -1) inQuotes = !inQuotes
}
}
return false;
fieldValue = trim(fieldValue);
return fieldValue;
}
// Appends terms to a query.
// @param query Form field of query
// @param termModifier Term modifier
// @param value Value to be appended. Tokenized by spaces,
// and termModifier will be applied to each token.
// @param fieldName Name of search field. Omit if not a field-specific query.
// @param groupModifier Modifier applied to each group of terms.
// @return query form field
function appendTerms(query, termModifier, value, fieldName, groupModifier)
{
if(typeof(groupModifier) == "undefined")
groupModifier = DEFAULT_GROUP_MODIFIER;
value = buildTerms(termModifier, value);
// not a field-specific search
if(fieldName == null || fieldName.indexOf(noFieldPrefix) != -1 || fieldName.length == 0)
{
if(groupModifier == NO_MODIFIER)
{
if(query.value.length == 0)
{
query.value = value;
}
else
{
query.value = query.value + ' ' + value;
}
}
else
{
if(query.value.length == 0)
{
query.value = groupModifier + '(' + value + ')';
}
else
{
query.value = query.value + ' ' + groupModifier + '(' + value + ')';
}
}
}
else
{
if(query.value.length == 0)
{
query.value = groupModifier + fieldName + ':(' + value + ')';
}
else
{
query.value = query.value + ' ' + groupModifier +fieldName + ':(' + value + ')';
}
}
query.value = trim(query.value)
return query;
}
// Obtain the value of a form field.
// @param field Form field
// @return Array of values, or string value depending on field type,
// or empty string if field param is undefined or null
function getFieldValue(field)
{
if(typeof(field[0]) != "undefined" && field[0].type=="checkbox")
if(field == null || typeof(field) == "undefined")
return "";
if(typeof(field) != "undefined" && typeof(field[0]) != "undefined" && field[0].type=="checkbox")
return getCheckedValues(field);
if(typeof(field[0]) != "undefined" && field[0].type=="radio")
if(typeof(field) != "undefined" && typeof(field[0]) != "undefined" && field[0].type=="radio")
return getRadioValue(field);
if(field.type.match("select*"))
if(typeof(field) != "undefined" && field.type.match("select*"))
return getSelectedValues(field);
return field.value;
if(typeof(field) != "undefined")
return field.value;
}
function getRadioValue(radio)
@ -172,45 +249,35 @@ function getSelectedValues (select) {
return r.join(VALUE_DELIMITER);
}
function addModifier(query, modifier, value)
function quote(value)
{
value = trim(value);
if(query.value.length == 0)
{
query.value = modifier + '(' + value + ')';
}
else
{
query.value = query.value + ' ' + modifier + '(' + value + ')';
}
return "\"" + trim(value) + "\"";
}
function addFieldWithModifier(query, modifier, field, fieldValue)
function contains(array, s)
{
fieldValue = trim(fieldValue);
if(query.value.length == 0)
for(var i=0; i<array.length; i++)
{
query.value = modifier + '(' + field + ':(' + fieldValue + '))';
}
else
{
query.value = query.value + ' ' + modifier + '(' + field + ':(' + fieldValue + '))';
if(s == array[i])
return true;
}
return false;
}
function trim(inputString) {
if (typeof inputString != "string") { return inputString; }
var temp = inputString;
// Replace whitespace with a single space
var pattern = /\s+/ig;
temp = temp.replace(pattern, " ");
// Trim
// Trim
pattern = /^(\s*)([\w\W]*)(\b\s*$)/;
if (pattern.test(temp)) { temp = temp.replace(pattern, "$2"); }
// run it another time through for words which don't end with a character or a digit
pattern = /^(\s*)([\w\W]*)(\s*$)/;
if (pattern.test(temp)) { temp = temp.replace(pattern, "$2"); }
return temp; // Return the trimmed string back to the user
}
}

View File

@ -0,0 +1,159 @@
<html>
<head>
<script language="JavaScript" src="c:/market/jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="luceneQueryConstructor.js"></script>
</head>
<body>
<script>
setAlertUser(false);
// additions to jsUnit
function assertTrue(comment, value)
{
return assertEquals(comment, true, value);
}
function assertFalse(comment, value)
{
return assertEquals(comment, false, value);
}
function testTrim()
{
assertEquals("Not trimming", "foo bar", trim(" foo bar"));
assertEquals("Not trimming", "foo bar", trim(" foo bar "));
assertEquals("Not trimming", "foo bar", trim(" foo bar "));
assertEquals("Not trimming", "foo bar", trim(" foo bar\n\t "));
assertEquals("Not trimming", "foo:(bar)", trim(" foo:(bar)"));
}
function testGetFieldValue()
{
var frm = document.testQueryConstructor;
// test radio buttons
frm.fooRadio[0].checked = true;
assertEquals("Wrong value for radio button", frm.fooRadio[0].value, getFieldValue(frm.fooRadio));
frm.fooRadio[0].checked = false;
frm.fooRadio[1].checked = true;
assertEquals("Wrong value for radio button", frm.fooRadio[1].value, getFieldValue(frm.fooRadio));
// test checkbox
frm.fooCheck[0].checked = true;
assertEquals("Wrong value for checkbox", frm.fooCheck[0].value, getFieldValue(frm.fooCheck));
frm.fooCheck[0].checked = false;
frm.fooCheck[1].checked = true;
assertEquals("Wrong value for checkbox", frm.fooCheck[1].value, getFieldValue(frm.fooCheck));
// test select
frm.fooSelect.options[0].selected = true;
assertEquals("Wrong value for select", frm.fooSelect.options[0].value, getFieldValue(frm.fooSelect));
frm.fooSelect.options[0].selected = false;
frm.fooSelect.options[1].selected = true;
assertEquals("Wrong value for checkbox", frm.fooSelect.options[1].value, getFieldValue(frm.fooCheck));
// test text field
assertEquals("Wrong value for text field", frm.fooText.value, getFieldValue(frm.fooText));
// test hidden field
assertEquals("Wrong value for hidden field", frm.fooHidden.value, getFieldValue(frm.fooHidden));
// test undefined field
assertEquals("Wrong value for undefined field", "", getFieldValue(frm.nonexistent));
assertEquals("Wrong value for undefined field", "", getFieldValue(null));
}
function testAppendTerms()
{
var frm = document.testQueryConstructor;
assertEquals("Not appending AND terms correctly", "+fooText:(+1)", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value, frm.fooText.name).value);
frm.query.value = ''
assertEquals("Not appending NOT terms correctly", "+fooText:(-1)", appendTerms(frm.query, NOT_MODIFIER, frm.fooText.value, frm.fooText.name).value);
frm.query.value = ''
assertEquals("Not appending OR terms correctly", "+fooText:(1)", appendTerms(frm.query, OR_MODIFIER, frm.fooText.value, frm.fooText.name).value);
frm.query.value = ''
frm.fooText.value = "\"my foo bar\""
assertEquals("Not appending AND terms correctly", "+fooText:(+\"my foo bar\" )", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value, frm.fooText.name).value);
frm.fooText.value = "1"
frm.query.value = ''
assertEquals("Not appending terms correctly", "-fooText:(+1)", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value, frm.fooText.name, NOT_MODIFIER).value);
frm.query.value = ''
frm.fooText.value = "1 2 3"
assertEquals("Not appending terms correctly", "+fooText:(+1 +2 +3)", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value, frm.fooText.name).value);
frm.query.value = ''
assertEquals("Not appending terms correctly", "-fooText:(1 2 3)", appendTerms(frm.query, OR_MODIFIER, frm.fooText.value, frm.fooText.name, NOT_MODIFIER).value);
frm.query.value = ''
assertEquals("Not appending terms correctly", "-fooText:(-1 -2 -3)", appendTerms(frm.query, NOT_MODIFIER, frm.fooText.value, frm.fooText.name, NOT_MODIFIER, NO_MODIFIER).value);
frm.query.value = ''
frm.fooText.value = "1"
assertEquals("Not appending terms correctly(without fields)", "+(+1)", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value).value);
frm.query.value = ''
assertEquals("Not appending terms correctly(without fields)", "+(+1)", appendTerms(frm.query, AND_MODIFIER, frm["noField-fooText"].value, "noField-fooText").value);
frm.query.value = ''
frm.fooText.value = "1 2 3"
assertEquals("Not appending terms correctly(without fields)", "-1 -2 -3", appendTerms(frm.query, NOT_MODIFIER, frm.fooText.value,"", NO_MODIFIER).value);
frm.query.value = ''
assertEquals("Not appending terms correctly(without fields)", "+(+1 +2 +3)", appendTerms(frm.query, AND_MODIFIER, frm.fooText.value).value);
frm.fooText.value = "1"
frm.query.value = ''
}
function testDoMakeQuery()
{
var frm = document.testQueryConstructor;
frm.or.value = "orValue";
assertEquals("Wrong query constructed", "or:(orValue)", doMakeQuery(frm.query).value);
frm.orModifier.value = " |+";
assertEquals("Wrong query constructed", "+or:(orValue)", doMakeQuery(frm.query).value);
frm.orModifier.value = " | ";
assertEquals("Wrong query constructed", "or:(orValue)", doMakeQuery(frm.query).value);
frm.orModifier.value = "";
assertEquals("Wrong query constructed", DEFAULT_GROUP_MODIFIER + "or:(" + DEFAULT_TERM_MODIFIER + "orValue)", doMakeQuery(frm.query).value);
}
function doANDTerms()
{
}
</script>
<form name="testQueryConstructor">
<input type="radio" name="fooRadio" value="1">
<input type="radio" name="fooRadio" value="2">
<input type="checkbox" name="fooCheck" value="1">
<input type="checkbox" name="fooCheck" value="2">
<select name="fooSelect">
<option value="1">
<option value="2">
</select>
<input type="text" name="fooText" value="1">
<input type="text" name="noField-fooText" value="1">
<input type="text" name="foobarText" value="1">
<input type="hidden" name="fooHidden" value="1">
<input type="password" name="fooPassword">
<input type="button" name="fooButton" value="I am a button">
<input type="submit" name="fooSubmit">
<input type="reset" name="fooReset">
<input type="file" name="fooFile">
<input type="hidden" name="andModifier" value="+">
<input type="hidden" name="and">
<input type="hidden" name="notModifier" value="-">
<input type="hidden" name="not">
<input type="hidden" name="orModifier" value=" | ">
<input type="hidden" name="or">
<input type="text" name="query">
</form>
</body>
</html>