You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2013/12/24 14:39:24 UTC

svn commit: r1553272 [9/10] - in /lucene/dev/branches/lucene5376/lucene/server: ./ plugins/ plugins/BinaryDocument/ plugins/BinaryDocument/src/ plugins/BinaryDocument/src/java/ plugins/BinaryDocument/src/java/org/ plugins/BinaryDocument/src/java/org/ap...

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestFacets.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestFacets.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestFacets.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestFacets.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,220 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Locale;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestFacets extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "price", "{type: float, sort: true, index: true, store: true}");
+    put(o, "longField", "{type: long, index: true, facet: numericRange}");
+    put(o, "id", "{type: int, store: true, postingsFormat: Memory}");
+    put(o, "date", "{type: atom, index: false, store: true}");
+    put(o, "dateFacet", "{type: atom, index: false, store: false, facet: hierarchy}");
+    put(o, "author", "{type: text, index: false, facet: flat, group: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("indexName", "index");
+    o2.put("fields", o);
+    send("registerFields", o2);
+  }
+
+  // Returns gen for the added document
+  private long addDocument(int id, String author, String body, float price, String date) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("body", body);
+    o.put("author", author);
+    o.put("price", price);
+    o.put("id", id);
+    o.put("date", date);
+    JSONArray path = new JSONArray();
+    o.put("dateFacet", path);
+    for(String part : date.split("/")) {
+      path.add(part);
+    }
+
+    JSONObject o2 = new JSONObject();
+    o2.put("indexName", "index");
+    o2.put("fields", o);
+    JSONObject result = send("addDocument", o2);
+    return getLong(result, "indexGen");
+  }
+
+  private JSONObject search(String query, long indexGen, String sortField, boolean reversed, boolean snippets, String groupField, String groupSortField) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("indexName", "index");
+    o.put("queryText", query);
+    if (indexGen != -1) {
+      JSONObject o2 = new JSONObject();
+      o.put("searcher", o2);
+      o2.put("indexGen", indexGen);
+    }
+
+    if (sortField != null) {
+      JSONObject sort = new JSONObject();
+      o.put("sort", sort);
+      sort.put("doDocScores", true);
+
+      JSONArray sortFields = new JSONArray();
+      sort.put("fields", sortFields);
+
+      JSONObject o2 = new JSONObject();
+      sortFields.add(o2);
+
+      o2.put("field", sortField);
+      o2.put("reverse", reversed);
+    }
+
+    if (groupField != null) {
+      String s = "{field: '" + groupField + "'";
+      if (groupSortField != null) {
+        s += ", sort: [{field: '" + groupSortField + "'}]";
+      }
+      s += "}";
+      put(o, "grouping", s);
+    }
+
+    put(o, "facets", "[{path: 'dateFacet', topN: 10}]");
+    put(o, "retrieveFields", "['id', 'date', 'price', {field: 'body', highlight: " + (snippets ? "snippets" : "whole") + "}]");
+
+    return send("search", o);
+  }
+
+  public void testFacets() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Bob", "this is a test", 10.99f, "2012/10/17");
+    addDocument(1, "Lisa", "this is a another test", 11.99f, "2012/10/1");
+    long gen = addDocument(2, "Frank", "this is a third test", 12.99f, "2010/10/1");
+    JSONObject o = search("test", gen, "price", false, true, null, null);
+    assertEquals(3, ((Number) o.get("totalHits")).intValue());
+
+    JSONArray hits = (JSONArray) o.get("hits");
+    assertEquals(3, hits.size());
+
+    JSONObject hit = (JSONObject) hits.get(0);
+    assertEquals(0, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2012/10/17", ((JSONObject) hit.get("fields")).get("date"));
+
+    hit = (JSONObject) hits.get(1);
+    assertEquals(1, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2012/10/1", ((JSONObject) hit.get("fields")).get("date"));
+
+    hit = (JSONObject) hits.get(2);
+    assertEquals(2, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2010/10/1", ((JSONObject) hit.get("fields")).get("date"));
+    JSONArray facets = getArray(o, "facets[0].counts");
+    assertEquals(3, facets.size());
+    assertEquals("[\"top\",3]", facets.get(0).toString());
+    assertEquals("[\"2012\",2]", facets.get(1).toString());
+    assertEquals("[\"2010\",1]", facets.get(2).toString());
+  }    
+
+  public void testFacetsReopen() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Bob", "this is a test", 10.99f, "2012/10/17");
+    addDocument(1, "Lisa", "this is a another test", 11.99f, "2012/10/1");
+    commit();
+
+    long gen = addDocument(2, "Frank", "this is a third test", 12.99f, "2010/10/1");
+    JSONObject o = search("test", gen, "price", false, true, null, null);
+    assertEquals(3, ((Number) o.get("totalHits")).intValue());
+
+    JSONArray hits = (JSONArray) o.get("hits");
+    assertEquals(3, hits.size());
+
+    JSONObject hit = (JSONObject) hits.get(0);
+    assertEquals(0, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2012/10/17", ((JSONObject) hit.get("fields")).get("date"));
+
+    hit = (JSONObject) hits.get(1);
+    assertEquals(1, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2012/10/1", ((JSONObject) hit.get("fields")).get("date"));
+
+    hit = (JSONObject) hits.get(2);
+    assertEquals(2, ((JSONObject) hit.get("fields")).get("id"));
+    assertEquals("2010/10/1", ((JSONObject) hit.get("fields")).get("date"));
+
+    JSONArray facets = getArray(o, "facets[0].counts");
+    assertEquals(3, facets.size());
+    assertEquals("[\"top\",3]", facets.get(0).toString());
+    assertEquals("[\"2012\",2]", facets.get(1).toString());
+    assertEquals("[\"2010\",1]", facets.get(2).toString());
+  }    
+
+
+  public void testDrillSideways() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {author: Bob}}");
+    send("addDocument", "{indexName: index, fields: {author: Lisa}}");
+    send("addDocument", "{indexName: index, fields: {author: Lisa}}");
+    send("addDocument", "{indexName: index, fields: {author: Tom}}");
+    send("addDocument", "{indexName: index, fields: {author: Tom}}");
+    long indexGen = getLong(send("addDocument", "{indexName: index, fields: {author: Tom}}"), "indexGen");
+
+    // Initial query:
+    JSONObject o = send("search", String.format(Locale.ROOT, "{indexName: index, query: MatchAllDocsQuery, facets: [{path: [author], topN: 10}], searcher: {indexGen: %d}}", indexGen));
+    assertEquals(6, o.get("totalHits"));
+    assertEquals("[[\"top\",0],[\"Tom\",3],[\"Lisa\",2],[\"Bob\",1]]", getArray(o, "facets[0].counts").toString());
+
+    // Now, single drill down:
+    o = send("search", String.format(Locale.ROOT, "{indexName: index, drillDowns: [{field: author, values: [Bob]}], query: MatchAllDocsQuery, facets: [{path: [author], topN: 10}], searcher: {indexGen: %d}}", indexGen));
+    assertEquals(1, o.get("totalHits"));
+    assertEquals("[[\"top\",0],[\"Tom\",3],[\"Lisa\",2],[\"Bob\",1]]", getArray(o, "facets[0].counts").toString());
+
+    // Multi drill down:
+    o = send("search", String.format(Locale.ROOT, "{indexName: index, drillDowns: [{field: author, values: [Bob, Lisa]}], query: MatchAllDocsQuery, facets: [{path: [author], topN: 10}], searcher: {indexGen: %d}}", indexGen));
+    assertEquals(3, o.get("totalHits"));
+    assertEquals("[[\"top\",0],[\"Tom\",3],[\"Lisa\",2],[\"Bob\",1]]", getArray(o, "facets[0].counts").toString());
+  }
+
+  public void testRangeFacets() throws Exception {
+    deleteAllDocs();    
+    long gen = -1;
+    for(int i=0;i<100;i++) {
+      gen = getLong(send("addDocument", "{indexName: index, fields: {longField: " + i + "}}"), "indexGen");
+    }
+    JSONObject o = send("search", "{indexName: index, facets: [{path: longField, numericRanges: [{label: All, min: 0, max: 99, minInclusive: true, maxInclusive: true}, {label: Half, min: 0, max: 49, minInclusive: true, maxInclusive: true}]}], searcher: {indexGen: " + gen + "}}");
+    assertEquals("All", getString(o, "facets[0].counts[1][0]"));
+    assertEquals(100, getInt(o, "facets[0].counts[1][1]"));
+    assertEquals("Half", getString(o, "facets[0].counts[2][0]"));
+    assertEquals(50, getInt(o, "facets[0].counts[2][1]"));
+  }
+}
+

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestGrouping.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestGrouping.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestGrouping.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestGrouping.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,255 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestGrouping extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "price", "{type: float, sort: true, index: true, store: true}");
+    put(o, "id", "{type: int, store: true, postingsFormat: Memory}");
+    put(o, "date", "{type: atom, index: false, store: true}");
+    put(o, "dateFacet", "{type: atom, index: false, store: false, facet: hierarchy}");
+    put(o, "author", "{type: text, index: false, facet: flat, group: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+    send("registerFields", o2);
+  }
+
+  // Returns gen for the added document
+  private long addDocument(int id, String author, String body, float price, String date) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("body", body);
+    o.put("author", author);
+    o.put("price", price);
+    o.put("id", id);
+    o.put("date", date);
+    JSONArray path = new JSONArray();
+    o.put("dateFacet", path);
+    for(String part : date.split("/")) {
+      path.add(part);
+    }
+
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+    JSONObject result = send("addDocument", o2);
+    return getLong(result, "indexGen");
+  }
+
+  public void testGrouping() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Lisa", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+    addDocument(0, "Tom", "this is a test.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+    addDocument(0, "Lisa", "this is a test.  this sentence has test twice test.", 10.99f, "2012/10/17");
+    long gen = addDocument(0, "Bob", "this is a test.", 10.99f, "2012/10/17");
+
+    JSONObject o2 = search("test", gen, null, false, false, "author", null);
+    assertEquals(4, ((Number) o2.get("totalHits")).intValue());
+    assertEquals(4, ((Number) o2.get("totalGroupedHits")).intValue());
+    JSONArray a = (JSONArray) o2.get("groups");
+    assertEquals(3, a.size());
+
+    assertEquals("Lisa", ((JSONObject) a.get(0)).get("groupValue"));
+    assertEquals(2, ((Number)((JSONObject) a.get(0)).get("totalHits")).intValue());
+
+    assertEquals("Tom", ((JSONObject) a.get(1)).get("groupValue"));
+    assertEquals(1, ((Number)((JSONObject) a.get(1)).get("totalHits")).intValue());
+
+    assertEquals("Bob", ((JSONObject) a.get(2)).get("groupValue"));
+    assertEquals(1, ((Number)((JSONObject) a.get(2)).get("totalHits")).intValue());
+
+    // Should be this:
+    /*
+{
+    "facets": [
+        {
+            "2012": 4
+        }
+    ],
+    "groups": [
+        {
+            "groupSortFields": {
+                "<score>": 0.7768564
+            },
+            "groupValue": "Bob",
+            "hits": [
+                {
+                    "doc": 3,
+                    "fields": {
+                        "body": "this is a <b>test</b>.",
+                        "date": "2012/10/17",
+                        "id": "0",
+                        "price": "10.99"
+                    },
+                    "score": 0.7768564
+                }
+            ],
+            "maxScore": 0.7768564,
+            "totalHits": 1
+        },
+        {
+            "groupSortFields": {
+                "<score>": 0.50458306
+            },
+            "groupValue": "Lisa",
+            "hits": [
+                {
+                    "doc": 2,
+                    "fields": {
+                        "body": "this is a <b>test</b>.  this sentence has <b>test</b> twice <b>test</b>.",
+                        "date": "2012/10/17",
+                        "id": "0",
+                        "price": "10.99"
+                    },
+                    "score": 0.50458306
+                },
+                {
+                    "doc": 0,
+                    "fields": {
+                        "body": "this is a <b>test</b>.  here is a random sentence.  here is another sentence with <b>test</b> in it.",
+                        "date": "2012/10/17",
+                        "id": "0",
+                        "price": "10.99"
+                    },
+                    "score": 0.3433253
+                }
+            ],
+            "maxScore": 0.50458306,
+            "totalHits": 2
+        },
+        {
+            "groupSortFields": {
+                "<score>": 0.4806554
+            },
+            "groupValue": "Tom",
+            "hits": [
+                {
+                    "doc": 1,
+                    "fields": {
+                        "body": "this is a <b>test</b>.  here is another sentence with <b>test</b> in it.",
+                        "date": "2012/10/17",
+                        "id": "0",
+                        "price": "10.99"
+                    },
+                    "score": 0.4806554
+                }
+            ],
+            "maxScore": 0.4806554,
+            "totalHits": 1
+        }
+    ],
+    "maxScore": 0.7768564,
+    "searchState": {
+        "lastDocID": 1,
+        "searcher": 25
+    },
+    "totalGroupedHits": 4,
+    "totalHits": 4
+}
+    */
+  }
+
+  private JSONObject search(String query, long indexGen, String sortField, boolean reversed, boolean snippets, String groupField, String groupSortField) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("indexName", "index");
+    o.put("queryText", query);
+    if (indexGen != -1) {
+      JSONObject o2 = new JSONObject();
+      o.put("searcher", o2);
+      o2.put("indexGen", indexGen);
+    }
+
+    if (sortField != null) {
+      JSONObject sort = new JSONObject();
+      o.put("sort", sort);
+      sort.put("doDocScores", true);
+
+      JSONArray sortFields = new JSONArray();
+      sort.put("fields", sortFields);
+
+      JSONObject o2 = new JSONObject();
+      sortFields.add(o2);
+
+      o2.put("field", sortField);
+      o2.put("reverse", reversed);
+    }
+
+    if (groupField != null) {
+      String s = "{field: '" + groupField + "'";
+      if (groupSortField != null) {
+        s += ", sort: [{field: '" + groupSortField + "'}]";
+      }
+      s += "}";
+      put(o, "grouping", s);
+    }
+
+    put(o, "facets", "[{path: 'dateFacet', topN: 10}]");
+    put(o, "retrieveFields", "['id', 'date', 'price', {field: 'body', highlight: " + (snippets ? "snippets" : "whole") + "}]");
+
+    return send("search", o);
+  }
+
+  public void testGroupingWithGroupSort() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Lisa", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 5.99f, "2010/10/17");
+    addDocument(0, "Tom", "this is a test.  here is another sentence with test in it.", 11.99f, "2011/10/17");
+    addDocument(0, "Lisa", "this is a test.  this sentence has test twice test.", 1.99f, "2012/10/17");
+    long gen = addDocument(0, "Bob", "this is a test.", 7.99f, "2013/10/17");
+
+    JSONObject o2 = search("test", gen, "price", false, false, "author", "price");
+    assertEquals(4, ((Number) o2.get("totalHits")).intValue());
+    assertEquals(4, ((Number) o2.get("totalGroupedHits")).intValue());
+    JSONArray a = (JSONArray) o2.get("groups");
+    assertEquals(3, a.size());
+
+    assertEquals("Lisa", ((JSONObject) a.get(0)).get("groupValue"));
+    assertEquals(2, ((Number)((JSONObject) a.get(0)).get("totalHits")).intValue());
+    assertNull(((JSONObject) a.get(0)).get("maxScore"));
+
+    assertEquals("Bob", ((JSONObject) a.get(1)).get("groupValue"));
+    assertEquals(1, ((Number)((JSONObject) a.get(1)).get("totalHits")).intValue());
+    assertNull(((JSONObject) a.get(1)).get("maxScore"));
+
+    assertEquals("Tom", ((JSONObject) a.get(2)).get("groupValue"));
+    assertEquals(1, ((Number)((JSONObject) a.get(2)).get("totalHits")).intValue());
+    assertNull(((JSONObject) a.get(2)).get("maxScore"));
+  }
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestHighlight.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestHighlight.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestHighlight.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestHighlight.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,196 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestHighlight extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "price", "{type: float, sort: true, index: true, store: true}");
+    put(o, "id", "{type: int, store: true, postingsFormat: Memory}");
+    put(o, "date", "{type: atom, index: false, store: true}");
+    put(o, "dateFacet", "{type: atom, index: false, store: false, facet: hierarchy}");
+    put(o, "author", "{type: text, index: false, facet: flat, group: true}");
+    // Register multi-valued field:
+    put(o, "authors", "{type: text, highlight: true, facet: flat, multiValued: true, analyzer: {matchVersion: LUCENE_43, class: StandardAnalyzer}}");
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+
+    send("registerFields", o2);
+  }
+
+  // Returns gen for the added document
+  private long addDocument(int id, String author, String body, float price, String date) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("body", body);
+    o.put("author", author);
+    o.put("price", price);
+    o.put("id", id);
+    o.put("date", date);
+    JSONArray path = new JSONArray();
+    o.put("dateFacet", path);
+    for(String part : date.split("/")) {
+      path.add(part);
+    }
+
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+    JSONObject result = send("addDocument", o2);
+    return getLong(result, "indexGen");
+  }
+
+  private JSONObject search(String query, long indexGen, String sortField, boolean reversed, boolean snippets, String groupField, String groupSortField) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("indexName", "index");
+    o.put("queryText", query);
+    if (indexGen != -1) {
+      JSONObject o2 = new JSONObject();
+      o.put("searcher", o2);
+      o2.put("indexGen", indexGen);
+    }
+
+    if (sortField != null) {
+      JSONObject sort = new JSONObject();
+      o.put("sort", sort);
+      sort.put("doDocScores", true);
+
+      JSONArray sortFields = new JSONArray();
+      sort.put("fields", sortFields);
+
+      JSONObject o2 = new JSONObject();
+      sortFields.add(o2);
+
+      o2.put("field", sortField);
+      o2.put("reverse", reversed);
+    }
+
+    if (groupField != null) {
+      String s = "{field: '" + groupField + "'";
+      if (groupSortField != null) {
+        s += ", sort: [{field: '" + groupSortField + "'}]";
+      }
+      s += "}";
+      put(o, "grouping", s);
+    }
+
+    put(o, "facets", "[{path: 'dateFacet', topN: 10}]");
+    put(o, "retrieveFields", "['id', 'date', 'price', {field: 'body', highlight: " + (snippets ? "snippets" : "whole") + "}]");
+
+    return send("search", o);
+  }
+
+  public void testHighlightSnippet() throws Exception {
+    deleteAllDocs();
+    long gen = addDocument(0, "Melanie", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+    JSONObject o = search("test", gen, null, false, true, null, null);
+
+    assertEquals("this is a <b>test</b>.  ...here is another sentence with <b>test</b> in it.",
+                 renderHighlight(getArray(o, "hits[0].fields.body")));
+  }
+
+  /** Highlight entire value as a single passage (eg good
+   *  for title fields). */
+  public void testWholeHighlight() throws Exception {
+    deleteAllDocs();
+    long gen = addDocument(0, "Lisa", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+    JSONObject o = search("test", gen, null, false, false, null, null);
+    assertEquals("this is a <b>test</b>.  here is a random sentence.  here is another sentence with <b>test</b> in it.",
+                 renderHighlight(getArray(o, "hits[0].fields.body")));
+  }
+
+  /** Make sure we can index a field with 3 values,
+   *  highlight it, and get back 3 values, each of them
+   *  separately highlighted (not a single value with the 3
+   *  values appended). */
+  public void testMultiValuedWholeHighlight() throws Exception {
+    deleteAllDocs();
+
+    long gen = addDocument("{indexName: index, fields: {authors: ['Dr. Seuss', 'Bob Smith', 'Seuss is Fun.  Some extra content.']}}");
+    JSONObject result = send("search", "{indexName: index, queryText: 'authors:seuss', retrieveFields: [{field: authors, highlight: whole}], searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+    JSONArray fields = getArray(result, "hits[0].fields.authors");
+    assertEquals(3, fields.size());
+    assertEquals("Dr. <b>Seuss</b>", renderSingleHighlight((JSONArray) fields.get(0)));
+    assertEquals("Bob Smith", renderSingleHighlight((JSONArray) fields.get(1)));
+    assertEquals("<b>Seuss</b> is Fun.  Some extra content.", renderSingleHighlight((JSONArray) fields.get(2)));
+  }
+
+  public void testMultiValuedSnippetHighlight() throws Exception {
+    deleteAllDocs();
+
+    long gen = addDocument("{indexName: index, fields: {authors: ['Dr. Seuss', 'Bob Smith', 'Seuss is Fun.  Some extra content.']}}");
+    JSONObject result = send("search", "{indexName: index, queryText: 'authors:seuss', retrieveFields: [{field: authors, highlight: snippets, maxPassages: 1}], searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals(1, getInt(result, "hits[0].fields.authors.length"));
+    assertEquals("<b>Seuss</b> Bob Smith <b>Seuss</b> is Fun.  ", renderSingleHighlight(getArray(result, "hits[0].fields.authors[0].parts")));
+  }
+  
+  /** Make sure we can use a different maxPassages per field */
+  public void testPerFieldMaxPassages() throws Exception {
+    deleteAllDocs();
+    long gen = addDocument("{indexName: index, fields: {body: 'This sentence has test.  This one does not.  Here is test again.', authors: ['This sentence has test.  This one does not.  Here is test again.']}}");
+    JSONObject result = send("search", "{indexName: index, queryText: 'test', retrieveFields: [{field: authors, highlight: snippets, maxPassages: 1}, {field: body, highlight: snippets, maxPassages: 2}], searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+
+    // Author has just 1 passage:
+    assertEquals(1, getInt(result, "hits[0].fields.authors.length"));
+    assertEquals("Here is <b>test</b> again.", renderHighlight(getArray(result, "hits[0].fields.authors")));
+
+    // Body has 2 passages:
+    assertEquals(2, getInt(result, "hits[0].fields.body.length"));
+    assertEquals("This sentence has <b>test</b>.  ...Here is <b>test</b> again.", renderHighlight(getArray(result, "hits[0].fields.body")));
+  }
+
+  /** We don't allow INFO_SEP (U+001F) to appear in
+   *  multi-valued highlight fields. */
+  public void testContentWithSep() throws Exception {
+    deleteAllDocs();
+    try {
+      addDocument("{indexName: index, fields: {authors: ['Dr. Seuss', 'Bob \u001F Smith', 'Seuss is Fun']}}");
+      fail("didn't hit exception");
+    } catch (IOException ioe) {
+      // expected
+    }
+  }
+}
+

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestIndexing.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestIndexing.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestIndexing.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestIndexing.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,267 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Locale;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.lucene.util._TestUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestIndexing extends ServerBaseTestCase {
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "id", "{type: atom, store: true, postingsFormat: Memory}");
+    put(o, "price", "{type: float, sort: true, index: true, store: true}");
+    put(o, "date", "{type: atom, index: false, store: true}");
+    put(o, "dateFacet", "{type: atom, index: false, store: false, facet: hierarchy}");
+    put(o, "author", "{type: text, index: false, facet: flat, store: true, group: true}");
+    put(o, "charCount", "{type: int, store: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("indexName", "index");
+    o2.put("fields", o);
+    send("registerFields", o2);
+  }
+
+  public void testUpdateDocument() throws Exception {
+    send("addDocument", "{indexName: index, fields: {body: 'here is a test', id: '0'}}");
+    long gen = getLong(send("updateDocument", "{indexName: index, term: {field: id, term: '0'}, fields: {body: 'here is another test', id: '0'}}"), "indexGen");
+    JSONObject o = send("search", "{indexName: index, queryText: 'body:test', searcher: {indexGen: " + gen + "}, retrieveFields: [body]}");
+    assertEquals(1, getInt(o, "totalHits"));
+    assertEquals("here is another test", getString(o, "hits[0].fields.body"));
+  }
+
+  public void testBulkUpdateDocuments() throws Exception {
+    deleteAllDocs();
+    StringBuilder sb = new StringBuilder();
+    sb.append("{\"indexName\": \"index\", \"documents\": [");
+    for(int i=0;i<100;i++) {
+      JSONObject o = new JSONObject();
+      o.put("body", "here is the body " + i);
+      o.put("id", ""+i);
+      if (i > 0) {
+        sb.append(',');
+      }
+      JSONObject o2 = new JSONObject();
+      o2.put("fields", o);
+      sb.append(o2.toString());
+    }
+    sb.append("]}");
+
+    String s = sb.toString();
+
+    JSONObject result = sendChunked(s, "bulkAddDocument");
+    assertEquals(100, result.get("indexedDocumentCount"));
+    long indexGen = ((Number) result.get("indexGen")).longValue();
+    assertEquals(1, getInt(send("search", "{indexName: index, queryText: 'body:99', searcher: {indexGen: " + indexGen + "}}"), "totalHits"));
+
+    // Now, update:
+    sb = new StringBuilder();
+    sb.append("{\"indexName\": \"index\", \"documents\": [");
+    for(int i=0;i<100;i++) {
+      JSONObject o2 = new JSONObject();
+      JSONObject o = new JSONObject();
+      o2.put("fields", o);
+      o.put("body", "here is the body " + i);
+      o.put("id", ""+i);
+      if (i > 0) {
+        sb.append(',');
+      }
+      put(o2, "term", "{field: id, term: '" + i + "'}");
+      sb.append(o2.toString());
+    }
+    sb.append("]}");
+
+    s = sb.toString();
+
+    result = sendChunked(s, "bulkUpdateDocument");
+    assertEquals(100, result.get("indexedDocumentCount"));
+    indexGen = ((Number) result.get("indexGen")).longValue();
+    assertEquals(1, getInt(send("search", "{indexName: index, queryText: 'body:99', searcher: {indexGen: " + indexGen + "}}"), "totalHits"));
+
+    assertEquals(100, getInt(send("search", "{indexName: index, query: MatchAllDocsQuery, searcher: {indexGen: " + indexGen + "}}"), "totalHits"));
+  }
+
+  public void testBulkAddException() throws Exception {
+    deleteAllDocs();
+    StringBuilder sb = new StringBuilder();
+    sb.append("{\"indexName\": \"index\", \"documents\": [");
+    for(int i=0;i<100;i++) {
+      JSONObject o = new JSONObject();
+      o.put("body", "here is the body " + i);
+      o.put("id", ""+i);
+      if (i > 0) {
+        sb.append(',');
+      }
+      if (i == 57) {
+        JSONArray broken = new JSONArray();
+        broken.add("2013");
+        broken.add("");
+        broken.add("17");
+        o.put("dateFacet", broken);
+      }
+      JSONObject o2 = new JSONObject();
+      o2.put("fields", o);
+      sb.append(o2.toString());
+    }
+    sb.append("]}");
+
+    String s = sb.toString();
+
+    try {
+      sendChunked(s, "bulkAddDocument");
+      fail("did not hit expected exception");
+    } catch (IOException ioe) {
+      // expected
+    }
+  }
+
+  private JSONObject search(String query, long indexGen, String sortField, boolean reversed, boolean snippets, String groupField, String groupSortField) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("indexName", "index");
+    o.put("queryText", query);
+    if (indexGen != -1) {
+      JSONObject o2 = new JSONObject();
+      o.put("searcher", o2);
+      o2.put("indexGen", indexGen);
+    }
+
+    if (sortField != null) {
+      JSONObject sort = new JSONObject();
+      o.put("sort", sort);
+      sort.put("doDocScores", true);
+
+      JSONArray sortFields = new JSONArray();
+      sort.put("fields", sortFields);
+
+      JSONObject o2 = new JSONObject();
+      sortFields.add(o2);
+
+      o2.put("field", sortField);
+      o2.put("reverse", reversed);
+    }
+
+    if (groupField != null) {
+      String s = "{field: '" + groupField + "'";
+      if (groupSortField != null) {
+        s += ", sort: [{field: '" + groupSortField + "'}]";
+      }
+      s += "}";
+      put(o, "grouping", s);
+    }
+
+    put(o, "facets", "[{path: 'dateFacet', topN: 10}]");
+    put(o, "retrieveFields", "['id', 'date', 'price', {field: 'body', highlight: " + (snippets ? "snippets" : "whole") + "}]");
+
+    return send("search", o);
+  }
+
+  public void testBulkAddDocument() throws Exception {
+    deleteAllDocs();
+    StringBuilder sb = new StringBuilder();
+    sb.append("{\"indexName\": \"index\", \"documents\": [");
+    for(int i=0;i<100;i++) {
+      JSONObject o = new JSONObject();
+      o.put("body", "here is the body " + i);
+      o.put("author", "Mr. " + i);
+      o.put("price", 15.66);
+      o.put("id", ""+i);
+      o.put("date", "01/01/2013");
+      if (i > 0) {
+        sb.append(",");
+      }
+      JSONObject o2 = new JSONObject();
+      o2.put("fields", o);
+      sb.append(o2.toString());
+    }
+    sb.append("]}");
+    String s = sb.toString();
+
+    JSONObject result = sendChunked(s, "bulkAddDocument");
+    assertEquals(100, result.get("indexedDocumentCount"));
+    long indexGen = getLong(result, "indexGen");
+    JSONObject r = search("99", indexGen, null, false, true, null, null);
+    assertEquals(1, ((Integer) r.get("totalHits")).intValue());
+  }
+
+  /** Make sure you get an error if you try to addDocument
+   *  after index is stopped */
+  public void testAddAfterStop() throws Exception {
+    deleteAllDocs();
+    send("stopIndex", "{indexName: index}");
+    try {
+      send("addDocument", "{indexName: index, fields: {}}");
+      fail();
+    } catch (IOException ioe) {
+      // expected
+    }
+    send("startIndex", "{indexName: index}");
+  }
+
+  public void testBoost() throws Exception {
+    _TestUtil.rmDir(new File("boost"));
+    send("createIndex", "{indexName: boost, rootDir: boost}");
+    send("settings", "{indexName: boost, directory: RAMDirectory, matchVersion: LUCENE_40}");
+    // Just to test index.ramBufferSizeMB:
+    send("liveSettings", "{indexName: boost, index.ramBufferSizeMB: 20.0}");
+    send("registerFields", "{indexName: boost, fields: {id: {type: atom, store: true}, body: {type: text, analyzer: StandardAnalyzer}}}");
+    send("startIndex", "{indexName: boost}");
+    send("addDocument", "{indexName: boost, fields: {id: '0', body: 'here is a test'}}");
+    long gen = getLong(send("addDocument", "{indexName: boost, fields: {id: '1', body: 'here is a test'}}"), "indexGen");
+    JSONObject result = send("search", String.format(Locale.ROOT, "{indexName: boost, retrieveFields: [id], queryText: test, searcher: {indexGen: %d}}", gen));
+    assertEquals(2, getInt(result, "hits.length"));
+    // Unboosted, the hits come back in order they were added:
+    assertEquals("0", getString(result, "hits[0].fields.id"));
+    assertEquals("1", getString(result, "hits[1].fields.id"));
+
+    // Do it again, this time setting higher boost for 2nd doc:
+    send("deleteAllDocuments", "{indexName: boost}");
+    send("addDocument", "{indexName: boost, fields: {id: '0', body: 'here is a test'}}");
+    gen = getLong(send("addDocument", "{indexName: boost, fields: {id: '1', body: {boost: 2.0, value: 'here is a test'}}}"), "indexGen");
+    result = send("search", String.format(Locale.ROOT, "{indexName: boost, retrieveFields: [id], queryText: test, searcher: {indexGen: %d}}", gen));
+    assertEquals(2, getInt(result, "hits.length"));
+    // Unboosted, the hits come back in order they were added:
+    assertEquals("1", getString(result, "hits[0].fields.id"));
+    assertEquals("0", getString(result, "hits[1].fields.id"));
+
+    send("stopIndex", "{indexName: boost}");
+    send("deleteIndex", "{indexName: boost}");
+  }
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestLiveValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestLiveValues.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestLiveValues.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestLiveValues.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,67 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestLiveValues extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    send("registerFields", "{indexName: index, fields: {id: {type: atom, store: true, postingsFormat: Memory}}}");
+    send("registerFields", "{indexName: index, fields: {value: {type: atom, index: false, store: true, liveValues: id}}}");
+  }
+
+  // nocommit testDeletions
+
+  public void testLiveFields() throws Exception {
+    JSONArray arr = new JSONArray();
+    for(int i=0;i<100;i++) {
+      send("addDocument", "{indexName: index, fields: {id: '" + i + "', value: 'value is " + i + "'}}");
+      arr.add("" + i);
+    }
+    JSONObject request = new JSONObject();
+    request.put("indexName", "index");
+    request.put("ids", arr);
+    request.put("field", "value");
+    
+    JSONObject o = send("liveValues", request);
+    arr = (JSONArray) o.get("values");
+    assertEquals(100, arr.size());
+    for(int i=0;i<100;i++) {
+      assertEquals("value is " + i, arr.get(i));
+    }
+  }
+}
\ No newline at end of file

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestNumericFields.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestNumericFields.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestNumericFields.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestNumericFields.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,69 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import org.apache.lucene.util._TestUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONObject;
+
+public class TestNumericFields extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "intNoSort", "{type: int, store: true}");
+    put(o, "intSort", "{type: int, sort: true, store: true}");
+    put(o, "floatNoSort", "{type: float, store: true}");
+    put(o, "floatSort", "{type: float, sort: true, store: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+    send("registerFields", o2);
+  }
+
+  public void testRetrieve() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {intNoSort: 17, intSort: 22, floatNoSort: 17.0, floatSort: 22.0}}"), "indexGen");
+    JSONObject result = send("search", "{indexName: index, retrieveFields: [intNoSort, intSort, floatNoSort, floatSort], query: MatchAllDocsQuery, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals(17, getInt(result, "hits[0].fields.intNoSort"));
+    assertEquals(22, getInt(result, "hits[0].fields.intSort"));
+    assertEquals(17.0f, getFloat(result, "hits[0].fields.floatNoSort"), 1e-7);
+    assertEquals(22.0f, getFloat(result, "hits[0].fields.floatSort"), 1e-7);
+  }
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestPlugins.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestPlugins.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestPlugins.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestPlugins.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,76 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Set;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestPlugins extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void init() throws Exception {
+    clearDir();
+    installPlugin(new File("../MockPlugin-0.1.zip"));
+    startServer();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+
+  // nocommit this test should install from zip file (call installPlugin(...))
+
+  public void testMockPlugin() throws Exception {
+
+    // Make sure docs reflect new mockFoobar:
+    String doc = httpLoad("doc?method=addDocument");
+    assertTrue(doc.indexOf("<b>mockFoobar</b>") != -1);
+
+    // nocommit test docs: verify foobar is there
+    // nocommit need a "list plugins" API: verify foobar is there
+    // nocommit send addDocument & verify change "took"
+
+    send("createIndex", "{indexName: index, rootDir: test}");
+    send("startIndex", "{indexName: index}");
+    send("registerFields", "{indexName: index, fields: {id: {type: int, store: true, postingsFormat: Memory}, intfield: {type: int, store: true}}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {id: 0, mockFoobar: 7}}"), "indexGen");
+
+    JSONObject result = send("search", "{indexName: index, searcher: {indexGen: " + gen + "}, query: MatchAllDocsQuery, retrieveFields: [id, intfield]}");
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals(14, getInt(result, "hits[0].fields.intfield"));
+    //System.out.println("got: " + prettyPrint(result));
+  }
+
+  /** Make sure a plugin can have/serve a static file. */
+  public void testStaticFile() throws Exception {
+    String contents = httpLoad("plugins/Mock/hello.txt");
+    assertEquals("hello world!\n", contents);
+  }
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSearch.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSearch.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSearch.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSearch.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,310 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import org.apache.lucene.util._TestUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONObject;
+
+public class TestSearch extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: WhitespaceAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "title", "{type: text, highlight: true, store: true, analyzer: {class: WhitespaceAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "id", "{type: int, store: true, sort: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+    send("registerFields", o2);
+  }
+
+  public void testPhraseQuery() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'the wizard of oz'}}"), "indexGen");
+    JSONObject result = send("search", "{indexName: index, query: {class: PhraseQuery, field: body, terms: [wizard, of, oz]}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+
+    result = send("search", "{indexName: index, query: {class: PhraseQuery, field: body, terms: [wizard, oz]}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(0, getInt(result, "totalHits"));
+
+    result = send("search", "{indexName: index, query: {class: PhraseQuery, field: body, terms: [wizard, oz], slop: 1}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+  }
+
+  public void testConstantScoreQuery() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'the wizard of oz'}}"), "indexGen");
+    JSONObject result = send("search", "{indexName: index, query: {class: TermQuery, field: body, term: wizard}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+
+    result = send("search", "{indexName: index, query: {class: ConstantScoreQuery, boost: 10.0, query: {class: TermQuery, field: body, term: wizard}}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals(10.0, getFloat(result, "hits[0].score"), .000001f);
+  }
+
+  public void testRegexpQuery() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'testing'}}"), "indexGen");
+    JSONObject r = send("search", "{indexName: index, query: {class: RegexpQuery, field: body, regexp: '.*est.*'}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(r, "totalHits"));
+    r = send("search", "{indexName: index, query: {class: RegexpQuery, field: body, regexp: '.*zest.*'}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(0, getInt(r, "totalHits"));
+  }
+
+  public void testTermRangeQuery() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {body: 'terma'}}");
+    send("addDocument", "{indexName: index, fields: {body: 'termb'}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'termc'}}"), "indexGen");
+
+    JSONObject result = send("search", "{indexName: index, query: {class: TermRangeQuery, field: body, lowerTerm: terma, upperTerm: termc, includeLower: true, includeUpper: true}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(3, getInt(result, "totalHits"));
+    result = send("search", "{indexName: index, query: {class: TermRangeQuery, field: body, lowerTerm: terma, upperTerm: termc, includeLower: false, includeUpper: false}, searcher: {indexGen: " + gen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+  }
+
+  public void testMatchAllDocsQuery() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {body: 'terma'}}");
+    send("addDocument", "{indexName: index, fields: {body: 'termb'}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'termc'}}"), "indexGen");
+    assertEquals(3, getInt(send("search", "{indexName: index, query: {class: MatchAllDocsQuery}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testWildcardQuery() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {body: 'terma'}}");
+    send("addDocument", "{indexName: index, fields: {body: 'termb'}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'termc'}}"), "indexGen");
+    assertEquals(3, getInt(send("search", "{indexName: index, query: {class: WildcardQuery, field: body, term: 'term?'}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testFuzzyQuery() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'fantastic'}}"), "indexGen");
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: FuzzyQuery, field: body, term: 'fantasic', maxEdits: 1}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: FuzzyQuery, field: body, term: 'fantasic', maxEdits: 2}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(0, getInt(send("search", "{indexName: index, query: {class: FuzzyQuery, field: body, term: 'fantasc', maxEdits: 1}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: FuzzyQuery, field: body, term: 'fantasc', maxEdits: 2}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: FuzzyQuery, field: body, term: 'fantasc', maxEdits: 2, prefixLength: 4}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testCommonTermsQuery() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {body: 'fantastic'}}");
+    send("addDocument", "{indexName: index, fields: {body: 'fantastic four'}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'fantastic five'}}"), "indexGen");
+
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: CommonTermsQuery, highFreqOccur: must, lowFreqOccur: must, maxTermFrequency: 0.5, field: body, terms: [fantastic, four]}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testMultiPhraseQuery() throws Exception {
+    deleteAllDocs();
+    send("addDocument", "{indexName: index, fields: {body: 'fantastic five is furious'}}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'fantastic four is furious'}}"), "indexGen");
+
+    assertEquals(1, getInt(send("search", "{indexName: index, query: {class: MultiPhraseQuery, field: body, terms: [fantastic, five, is, furious]}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(2, getInt(send("search", "{indexName: index, query: {class: MultiPhraseQuery, field: body, terms: [fantastic, {term: furious, position: 3}]}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(2, getInt(send("search", "{indexName: index, query: {class: MultiPhraseQuery, field: body, terms: [fantastic, [five, four], {term: furious, position: 3}]}, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testClassicQPDefaultOperator() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'fantastic four is furious'}}"), "indexGen");
+    
+    assertEquals(1, getInt(send("search", "{indexName: index, queryParser: {class: classic, defaultOperator: or, defaultField: body}, queryText: 'furious five', searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(0, getInt(send("search", "{indexName: index, queryParser: {class: classic, defaultOperator: and, defaultField: body}, queryText: 'furious five', searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testMultiFieldQP() throws Exception {
+    deleteAllDocs();
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'fantastic four is furious', title: 'here is the title'}}"), "indexGen");
+    
+    assertEquals(1, getInt(send("search", "{indexName: index, queryParser: {class: MultiFieldQueryParser, defaultOperator: or, fields: [body, {field: title, boost: 2.0}]}, queryText: 'title furious', searcher: {indexGen: " + gen + "}}"), "totalHits"));
+    assertEquals(1, getInt(send("search", "{indexName: index, queryParser: {class: MultiFieldQueryParser, defaultOperator: and, fields: [body, {field: title, boost: 2.0}]}, queryText: 'title furious', searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+
+  public void testNumericRangeQuery() throws Exception {
+    for(String type : new String[] {"int", "long", "float", "double"}) {
+      send("createIndex", "{indexName: nrq, rootDir: nrq}");
+      send("startIndex", "{indexName: nrq}");
+      send("registerFields", String.format("{indexName: nrq, fields: {nf: {type: %s, index: true}}}", type));
+      send("addDocument", "{indexName: nrq, fields: {nf: 5}}");
+      send("addDocument", "{indexName: nrq, fields: {nf: 10}}");
+      long gen = getLong(send("addDocument", "{indexName: nrq, fields: {nf: 17}}"), "indexGen");
+
+      // Both min & max:
+      assertEquals(3, getInt(send("search",
+
+                                  String.format(Locale.ROOT, "{indexName: nrq, query: {class: NumericRangeQuery, field: nf, min: 5, max: 17, minInclusive: true, maxInclusive: true}, searcher: {indexGen: %d}}", gen)),
+                             "totalHits"));
+
+      // Leave min out:
+      assertEquals(3, getInt(send("search",
+                                  String.format(Locale.ROOT, "{indexName: nrq, query: {class: NumericRangeQuery, field: nf, max: 17, maxInclusive: true}, searcher: {indexGen: %d}}", gen)),
+                             "totalHits"));
+
+      // Leave min out, don't include max:
+      assertEquals(2, getInt(send("search",
+                                  String.format(Locale.ROOT, "{indexName: nrq, query: {class: NumericRangeQuery, field: nf, max: 17, maxInclusive: false}, searcher: {indexGen: %d}}", gen)),
+                             "totalHits"));
+
+      // Leave max out:
+      assertEquals(3, getInt(send("search",
+                                  String.format(Locale.ROOT, "{indexName: nrq, query: {class: NumericRangeQuery, field: nf, min: 5, minInclusive: true}, searcher: {indexGen: %d}}", gen)),
+                             "totalHits"));
+
+      // Leave max out, don't include max:
+      assertEquals(2, getInt(send("search",
+                                  String.format(Locale.ROOT, "{indexName: nrq, query: {class: NumericRangeQuery, field: nf, min: 5, minInclusive: false}, searcher: {indexGen: %d}}", gen)),
+                             "totalHits"));
+      send("stopIndex", "{indexName: nrq}");
+      send("deleteIndex", "{indexName: nrq}");
+    }
+  }
+
+  public void testSearchAfter() throws Exception {
+    deleteAllDocs();
+    long gen = 0;
+    for(int i=0;i<20;i++) {
+      gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'this is the body', id: " + i + "}}"), "indexGen");
+    }
+
+    JSONObject lastPage = null;
+
+    Set<Integer> seenIDs = new HashSet<Integer>();
+
+    // Pull 4 pages with 5 hits per page:
+    for(int i=0;i<4;i++) {
+      String sa;
+      if (lastPage != null) {
+        sa = ", searchAfter: {lastDoc: " + getInt(lastPage, "searchState.lastDoc") + ", lastScore: " + getFloat(lastPage, "searchState.lastScore") + "}";
+      } else {
+        sa = "";
+      }
+
+      lastPage = send("search", "{indexName: index, query: MatchAllDocsQuery, topHits: 5, retrieveFields: [id], searcher: {indexGen: " + gen + "}" + sa + "}");
+      //System.out.println("i=" + i + ": " + lastPage);
+
+      // 20 total hits
+      assertEquals(20, getInt(lastPage, "totalHits"));
+      assertEquals(5, getInt(lastPage, "hits.length"));
+      for(int j=0;j<5;j++) {
+        seenIDs.add(getInt(lastPage, "hits[" + j + "].fields.id"));
+      }
+    }
+
+    assertEquals(20, seenIDs.size());
+  }
+
+  public void testSearchAfterWithSort() throws Exception {
+    deleteAllDocs();
+    long gen = 0;
+    for(int i=0;i<20;i++) {
+      gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'this is the body', id: " + i + "}}"), "indexGen");
+    }
+
+    JSONObject lastPage = null;
+
+    Set<Integer> seenIDs = new HashSet<Integer>();
+
+    // Pull 4 pages with 5 hits per page:
+    for(int i=0;i<4;i++) {
+      String sa;
+      JSONObject o = new JSONObject();
+      o.put("indexName", "index");
+      o.put("query", "MatchAllDocsQuery");
+      o.put("topHits", 5);
+      put(o, "retrieveFields", "[id]");
+      put(o, "sort", "{fields: [{field: id}]}");
+      put(o, "searcher", "{indexGen: " + gen + "}");
+      if (lastPage != null) {
+        JSONObject o2 = new JSONObject();
+        o.put("searchAfter", o2);
+        o2.put("lastDoc", getInt(lastPage, "searchState.lastDoc"));
+        o2.put("lastFieldValues", get(lastPage, "searchState.lastFieldValues"));
+      } else {
+        sa = "";
+      }
+
+      lastPage = send("search", o);
+
+      // 20 total hits
+      assertEquals(20, getInt(lastPage, "totalHits"));
+      assertEquals(5, getInt(lastPage, "hits.length"));
+      for(int j=0;j<5;j++) {
+        seenIDs.add(getInt(lastPage, "hits[" + j + "].fields.id"));
+      }
+    }
+
+    assertEquals(20, seenIDs.size());
+  }
+
+  public void testRecencyBlendedSort() throws Exception {
+    _TestUtil.rmDir(new File("recency"));
+    send("createIndex", "{indexName: recency, rootDir: recency}");
+    send("startIndex", "{indexName: recency}");
+    send("registerFields", "{indexName: recency, fields: {timestamp: {type: long, index: false, sort: true}, body: {type: text, analyzer: StandardAnalyzer}, blend: {type: virtual, recencyScoreBlend: {timeStampField: timestamp, maxBoost: 2.0, range: 30}}}}");
+
+    long t = System.currentTimeMillis()/1000;
+    send("addDocument", "{indexName: recency, fields: {body: 'this is some text', timestamp: " + (t-100) + "}}");
+    long gen = getLong(send("addDocument", "{indexName: recency, fields: {body: 'this is some text', timestamp: " + t + "}}"), "indexGen");
+
+    for(int pass=0;pass<2;pass++) {
+      // Unboosted:
+      JSONObject result = send("search", "{indexName: recency, queryText: text, searcher: {indexGen: " + gen + "}}");
+      assertEquals(2, getInt(result, "totalHits"));
+      assertEquals(0, getInt(result, "hits[0].doc"));
+      assertEquals(1, getInt(result, "hits[1].doc"));
+
+      // Relevance + recency changes the order:
+      result = send("search", "{indexName: recency, queryText: text, sort: {fields: [{field: blend}]}, searcher: {indexGen: " + gen + "}}");
+      assertEquals(2, getInt(result, "totalHits"));
+      assertEquals(1, getInt(result, "hits[0].doc"));
+      assertEquals(0, getInt(result, "hits[1].doc"));
+
+      // Make sure this survives restart:
+      send("stopIndex", "{indexName: recency}");
+      send("startIndex", "{indexName: recency}");
+    }
+  }
+
+  // nocommit test grouping
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestServer.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestServer.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestServer.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestServer.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,394 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONArray;
+import net.minidev.json.JSONObject;
+
+public class TestServer extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    JSONObject o = new JSONObject();
+    put(o, "body", "{type: text, highlight: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, similarity: {class: BM25Similarity, b: 0.15}}");
+    put(o, "id", "{type: int, store: true, postingsFormat: Memory}");
+    put(o, "price", "{type: float, sort: true, index: true, store: true}");
+    put(o, "date", "{type: atom, index: false, store: true}");
+    put(o, "dateFacet", "{type: atom, index: false, store: false, facet: hierarchy}");
+    put(o, "author", "{type: text, index: false, facet: flat, group: true}");
+    JSONObject o2 = new JSONObject();
+    o2.put("indexName", "index");
+    o2.put("fields", o);
+    send("registerFields", o2);
+  }
+
+  // nocommit multi-valued field
+
+  // Returns gen for the added document
+  private long addDocument(int id, String author, String body, float price, String date) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("body", body);
+    o.put("author", author);
+    o.put("price", price);
+    o.put("id", id);
+    o.put("date", date);
+    JSONArray path = new JSONArray();
+    o.put("dateFacet", path);
+    for(String part : date.split("/")) {
+      path.add(part);
+    }
+
+    JSONObject o2 = new JSONObject();
+    o2.put("fields", o);
+    o2.put("indexName", "index");
+
+    JSONObject result = send("addDocument", o2);
+    return ((Number) result.get("indexGen")).longValue();
+  }
+
+  // nocommit add test making sure we catch extra unused params
+
+  private JSONObject search(String body) throws Exception {
+    return search(body, -1, null, false, true, null, null);
+  }
+
+  private JSONObject search(String query, long indexGen, String sortField, boolean reversed, boolean snippets, String groupField, String groupSortField) throws Exception {
+    JSONObject o = new JSONObject();
+    o.put("indexName", "index");
+    o.put("queryText", query);
+    if (indexGen != -1) {
+      JSONObject o2 = new JSONObject();
+      o.put("searcher", o2);
+      o2.put("indexGen", indexGen);
+    }
+
+    if (sortField != null) {
+      JSONObject sort = new JSONObject();
+      o.put("sort", sort);
+      sort.put("doDocScores", true);
+
+      JSONArray sortFields = new JSONArray();
+      sort.put("fields", sortFields);
+
+      JSONObject o2 = new JSONObject();
+      sortFields.add(o2);
+
+      o2.put("field", sortField);
+      o2.put("reverse", reversed);
+    }
+
+    if (groupField != null) {
+      String s = "{field: '" + groupField + "'";
+      if (groupSortField != null) {
+        s += ", sort: [{field: '" + groupSortField + "'}]";
+      }
+      s += "}";
+      put(o, "grouping", s);
+    }
+
+    put(o, "facets", "[{path: 'dateFacet', topN: 10}]");
+    put(o, "retrieveFields", "['id', 'date', 'price', {field: 'body', highlight: " + (snippets ? "snippets" : "whole") + "}]");
+
+    return send("search", o);
+  }
+
+  public void testBasic() throws Exception {
+    deleteAllDocs();
+    long gen = addDocument(0, "Bob", "this is a test", 10f, "2012/10/17");
+    JSONObject o = search("test", gen, null, false, true, null, null);
+    assertEquals(1, getInt(o, "totalHits"));
+  }
+
+  public void testNumericSort() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Lisa", "this is a test", 10.99f, "2012/10/1");
+    long gen = addDocument(1, "Tom", "this is also a test", 14.99f, "2012/11/3");
+    JSONObject o = search("test", gen, "price", false, true, null, null);
+    assertEquals(2, ((Number) o.get("totalHits")).intValue());
+    JSONArray hits = (JSONArray) o.get("hits");
+    assertEquals(2, hits.size());
+
+    JSONObject hit = (JSONObject) hits.get(0);
+    assertEquals(0, ((JSONObject) hit.get("fields")).get("id"));
+
+    hit = (JSONObject) hits.get(1);
+    assertEquals(1, ((JSONObject) hit.get("fields")).get("id"));
+  }
+
+  public void testReverseNumericSort() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Frank", "this is a test", 10.99f, "2012/10/1");
+    long gen = addDocument(1, "Lisa", "this is also a test", 14.99f, "2012/11/3");
+    JSONObject o = search("test", gen, "price", true, true, null, null);
+    assertEquals(2, ((Number) o.get("totalHits")).intValue());
+
+    JSONArray hits = (JSONArray) o.get("hits");
+    assertEquals(2, hits.size());
+
+    JSONObject hit = (JSONObject) hits.get(0);
+    assertEquals(1, ((JSONObject) hit.get("fields")).get("id"));
+
+    hit = (JSONObject) hits.get(1);
+    assertEquals(0, ((JSONObject) hit.get("fields")).get("id"));
+  }
+
+  public void testPrevSearchState() throws Exception {
+    deleteAllDocs();
+    long gen = addDocument(0, "Tom", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+
+    JSONObject o = search("test", gen, null, false, false, null, null);
+    assertEquals(1, ((Number) o.get("totalHits")).intValue());
+
+    // Add another document
+    gen = addDocument(0, "Melanie", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+
+    JSONObject o2 = search("test", gen, null, false, false, null, null);
+    assertEquals(2, ((Number) o2.get("totalHits")).intValue());
+
+    // Now the first search does a follow-on search, so we
+    // should only see 1 document since it should be using
+    // the old searcher:
+    JSONObject o3 = new JSONObject();
+    o3.put("indexName", "index");
+    o3.put("queryText", "test");
+    put(o3, "searcher", "{version: " + get(o, "searchState.searcher") + "}");
+    //System.out.println("send: " + o3);
+    JSONObject o4 = send("search", o3);
+
+    assertEquals(1, ((Number) o4.get("totalHits")).intValue());
+  }
+
+  public void testInvalidFields() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Lisa", "this is a test.  here is a random sentence.  here is another sentence with test in it.", 10.99f, "2012/10/17");
+
+    JSONObject o3 = new JSONObject();
+    o3.put("queryText", "test");
+    JSONArray fields = new JSONArray();
+    o3.put("retrieveFields", fields);
+    fields.add("bogus");
+    try {
+      send("search", o3);
+      fail("did not hit exception");
+    } catch (IOException e) {
+      // expected
+    }
+
+    o3 = new JSONObject();
+    o3.put("queryText", "test");
+    JSONObject sort = new JSONObject();
+    o3.put("sort", sort);
+    JSONArray sortFields = new JSONArray();
+    sort.put("fields", sortFields);
+    
+    JSONObject sortField = new JSONObject();
+    sortFields.add(sortField);
+    sortField.put("field", "bogus2");
+    try {
+      send("search", o3);
+      fail("did not hit exception");
+    } catch (IOException e) {
+      // expected
+    }
+  }
+
+  public void testInvalidSearcherVersion() throws Exception {
+    deleteAllDocs();
+
+    JSONObject o3 = new JSONObject();
+    o3.put("queryText", "test");
+    JSONObject searchState = new JSONObject();
+    o3.put("priorSearchState", searchState);
+    searchState.put("searcher", 0);
+    searchState.put("lastDocID", 0);
+    try {
+      send("search", o3);
+      fail("didn't hit exception");
+    } catch (IOException e) {
+      // expected
+      //e.printStackTrace(System.out);
+    }
+  }
+
+  public void testMultiValuedString() throws Exception {
+    deleteAllDocs();
+
+    send("registerFields", "{indexName: index, fields: {authors: {type: text, index: true, store: true, facet: flat, multiValued: true, analyzer: {matchVersion: LUCENE_43, class: StandardAnalyzer}}}}");
+
+    JSONObject result = send("addDocument", "{indexName: index, fields: {authors: [Bob, Lisa]}}");
+
+    long indexGen = getInt(result, "indexGen");
+
+    result = send("search", "{indexName: index, searcher: {indexGen: " + indexGen + "}, queryText: 'authors:bob', retrieveFields: [authors]}");
+
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals("[\"Bob\",\"Lisa\"]", getArray(result, "hits[0].fields.authors").toString());
+  }
+
+  public void testMultiValuedNumeric() throws Exception {
+    deleteAllDocs();
+
+    send("registerFields", "{indexName: index, fields: {ratings: {type: int, index: true, store: true, multiValued: true}}}");
+
+    JSONObject result = send("addDocument", "{indexName: index, fields: {body: 'here is a test', ratings: [17, 22]}}");
+
+    long indexGen = getInt(result, "indexGen");
+
+    result = send("search", "{indexName: index, searcher: {indexGen: " + indexGen + "}, queryText: 'body:test', retrieveFields: [ratings]}");
+
+    assertEquals(1, getInt(result, "totalHits"));
+    assertEquals("[17,22]", getArray(result, "hits[0].fields.ratings").toString());
+  }
+
+  public void testStandardAnalyzer() throws Exception {
+    deleteAllDocs();
+
+    send("registerFields", "{indexName: index, fields: {aTextField: {type: text, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43}, store: true}}}");
+
+    JSONObject result = send("addDocument", "{indexName: index, fields: {aTextField: 'here is a test'}}");
+    long indexGen = getInt(result, "indexGen");
+
+    // nocommit: grrr need QP to understand schema
+    //o.put("queryText", "ratings:[16 TO 18]");
+
+    // search on a stop word should yield no results:
+    result = send("search", String.format(Locale.ROOT, "{indexName: index, searcher: {indexGen: %d}, queryText: 'aTextField:a'}", indexGen));
+    assertEquals(0, getInt(result, "totalHits"));
+  }
+
+  public void testStandardAnalyzerNoStopWords() throws Exception {
+    deleteAllDocs();
+
+    send("registerFields", "{indexName: index, fields: {aTextField2: {type: text, index: true, store: true, analyzer: {class: StandardAnalyzer, matchVersion: LUCENE_43, stopWords: []}}}}");
+
+    JSONObject result = send("addDocument", "{indexName: index, fields: {aTextField2: 'here is a test'}}");
+    long indexGen = getLong(result, "indexGen");
+
+    // nocommit: grrr need QP to understand schema
+    //o.put("queryText", "ratings:[16 TO 18]");
+
+    // search on a stop word should now yield one hit:
+    result = send("search", "{indexName: index, queryText: 'aTextField2:a', searcher: {indexGen: " + indexGen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+  }
+
+  public void testEnglishAnalyzerNoStopWords() throws Exception {
+    deleteAllDocs();
+
+    send("registerFields", "{indexName: index, fields: {aTextField3: {type: text, index: true, store: true, analyzer: {class: EnglishAnalyzer, matchVersion: LUCENE_43, stopWords: []}}}}");
+    JSONObject result = send("addDocument", "{indexName: index, fields: {aTextField3: 'the cats in the hat'}}");
+    long indexGen = getLong(result, "indexGen");
+
+    // nocommit: grrr need QP to understand schema
+    //o.put("queryText", "ratings:[16 TO 18]");
+
+    // cats should stem to cat and get a match:
+    result = send("search", "{indexName: index, queryText: 'aTextField3:cat', searcher: {indexGen: " + indexGen + "}}");
+    assertEquals(1, getInt(result, "totalHits"));
+  }
+
+  public void testInvalidFieldName() throws Exception {
+    JSONObject o = new JSONObject();
+    JSONObject o2 = new JSONObject();
+    o.put("9", o2);
+    o2.put("type", "text");
+    try {
+      send("registerFields", o);
+      fail();
+    } catch (IOException ioe) {
+      // expected
+    }
+  }
+
+  public void testMoreThanOneValueOnSingleValuedField() throws Exception {
+    deleteAllDocs();
+    JSONObject o = new JSONObject();
+    JSONArray arr = new JSONArray();
+    o.put("author", arr);
+    arr.add("Author 1");
+    arr.add("Author 2");
+
+    try {
+      send("addDocument", o);
+      fail("expected exception");
+    } catch (IOException ioe) {
+      // expected
+    }
+  }
+
+  public void testServerRestart() throws Exception {
+    deleteAllDocs();
+    addDocument(0, "Bob", "this is a test", 10f, "2012/10/17");
+    send("commit", "{indexName: index}");
+    shutdownServer();
+    startServer();
+    send("startIndex", "{indexName: index}");
+    JSONObject o = search("test", 0, null, false, true, null, null);
+    assertEquals(1, ((Number) o.get("totalHits")).intValue());
+  }
+
+  public void testStatsHandler() throws Exception {
+    JSONObject result = send("stats", "{indexName: index}");
+    //System.out.println("GOT: " + result);
+  }
+
+  public void testStuffAfterJSON() throws Exception {
+    // Extra whitespace should be OK:
+    sendRaw("stats", "{\"indexName\": \"index\"}  ");
+    
+    // ... but this should not:
+    try {
+      sendRaw("stats", "{\"indexName\": \"index\"}  bogus");
+      fail("did not hit exception");
+    } catch (IOException ioe) {
+      // expected
+      assertTrue(ioe.toString().indexOf("could not parse HTTP request data as JSON") != -1);
+    }
+  }
+
+  // nocommit assert that exact field name w/ error is in
+  // error message
+
+  // nocommit drillDowns test, single and multi valued
+
+  // nocommit testDocs
+
+  // nocommit need test case that screws up adding bulk docs
+  // (eg category path with empty string component) and
+  // verifies the error "comes through"
+}

Added: lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSettings.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSettings.java?rev=1553272&view=auto
==============================================================================
--- lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSettings.java (added)
+++ lucene/dev/branches/lucene5376/lucene/server/src/test/org/apache/lucene/server/TestSettings.java Tue Dec 24 13:39:22 2013
@@ -0,0 +1,59 @@
+package org.apache.lucene.server;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import net.minidev.json.JSONObject;
+
+public class TestSettings extends ServerBaseTestCase {
+
+  @BeforeClass
+  public static void initClass() throws Exception {
+    clearDir();
+    startServer();
+    createAndStartIndex();
+    registerFields();
+    commit();
+  }
+
+  @AfterClass
+  public static void fini() throws Exception {
+    shutdownServer();
+    System.clearProperty("sun.nio.ch.bugLevel"); // hack WTF
+  }
+
+  private static void registerFields() throws Exception {
+    send("registerFields", "{indexName: index, fields: {body: {type: text, analyzer: StandardAnalyzer}}}");
+  }
+
+  public void testNRTCachingDirSettings() throws Exception {
+    deleteAllDocs();
+    commit();
+    send("stopIndex", "{indexName: index}");
+    JSONObject o = send("settings", "{indexName: index}");
+    assertEquals(0, o.size());
+    // Turn off NRTCachingDir:
+    send("settings", "{indexName: index, nrtCachingDirectory.maxMergeSizeMB: 0.0, nrtCachingDirectory.maxSizeMB: 0.0}");
+    o = send("settings", "{indexName: index}");
+    assertEquals(2, o.size());
+    send("startIndex", "{indexName: index}");
+    long gen = getLong(send("addDocument", "{indexName: index, fields: {body: 'here is a test'}}"), "indexGen");
+    assertEquals(1, getInt(send("search", "{indexName: index, queryText: test, searcher: {indexGen: " + gen + "}}"), "totalHits"));
+  }
+}