org.apache.solr.response.transform.TestSubQueryTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.response.transform.TestSubQueryTransformer.java

Source

/*
 * 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.
 */
package org.apache.solr.response.transform;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

import org.apache.commons.io.output.ByteArrayOutputStream;

/*
 * 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.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.JavaBinCodec;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.BinaryQueryResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestSubQueryTransformer extends SolrTestCaseJ4 {
    private static int peopleMultiplier;
    private static int deptMultiplier;

    @BeforeClass
    public static void beforeTests() throws Exception {
        System.setProperty("enable.update.log", "false");
        initCore("solrconfig-basic.xml", "schema-docValuesJoin.xml");
        peopleMultiplier = atLeast(1);
        deptMultiplier = atLeast(1);

        int id = 0;
        for (int p = 0; p < peopleMultiplier; p++) {
            assertU(add(doc("id", "" + id++, "name_s", "john", "title_s", "Director", "dept_ss_dv", "Engineering",
                    "dept_i", "0", "dept_is", "0")));
            assertU(add(doc("id", "" + id++, "name_s", "mark", "title_s", "VP", "dept_ss_dv", "Marketing", "dept_i",
                    "1", "dept_is", "1")));
            assertU(add(doc("id", "" + id++, "name_s", "nancy", "title_s", "MTS", "dept_ss_dv", "Sales", "dept_i",
                    "2", "dept_is", "2")));
            assertU(add(doc("id", "" + id++, "name_s", "dave", "title_s", "MTS", "dept_ss_dv", "Support",
                    "dept_ss_dv", "Engineering", "dept_i", "3", "dept_is", "3", "dept_is", "0")));
            assertU(add(doc("id", "" + id++, "name_s", "tina", "title_s", "VP", "dept_ss_dv", "Engineering",
                    "dept_i", "0", "dept_is", "0")));

            if (rarely()) {
                assertU(commit("softCommit", "true"));
            }
        }

        for (int d = 0; d < deptMultiplier; d++) {
            assertU(add(doc("id", "" + id, "id_i", "" + id++, "dept_id_s", "Engineering", "text_t",
                    "These guys develop stuff", "salary_i_dv", "1000", "dept_id_i", "0")));
            assertU(add(doc("id", "" + id++, "id_i", "" + id++, "dept_id_s", "Marketing", "text_t",
                    "These guys make you look good", "salary_i_dv", "1500", "dept_id_i", "1")));
            assertU(add(doc("id", "" + id, "id_i", "" + id++, "dept_id_s", "Sales", "text_t",
                    "These guys sell stuff", "salary_i_dv", "1600", "dept_id_i", "2")));
            assertU(add(doc("id", "" + id, "id_i", "" + id++, "dept_id_s", "Support", "text_t",
                    "These guys help customers", "salary_i_dv", "800", "dept_id_i", "3")));

            if (rarely()) {
                assertU(commit("softCommit", "true"));
            }
        }
        assertU(commit());

    }

    @Test
    public void testJohnOrNancySingleField() throws Exception {
        //System.out.println("p "+peopleMultiplier+" d "+deptMultiplier);
        assertQ("subq1.fl is limited to single field",
                req("q", "name_s:(john nancy)", "indent", "true", "fl", "dept_ss_dv,name_s_dv,depts:[subquery]",
                        "rows", "" + (2 * peopleMultiplier), "depts.q", "{!term f=dept_id_s v=$row.dept_ss_dv}",
                        "depts.fl", "text_t", "depts.indent", "true", "depts.rows", "" + deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'][@numFound='"
                        + deptMultiplier + "']/doc/str[@name='text_t'][.='These guys develop stuff'])="
                        + (peopleMultiplier * deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts'][@numFound='"
                        + deptMultiplier + "']/doc/str[@name='text_t'][.='These guys sell stuff'])="
                        + (peopleMultiplier * deptMultiplier),
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc[1]/*)=1",
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[1]/result[@name='depts']/doc["
                        + deptMultiplier + "]/*)=1",
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[" + peopleMultiplier
                        + "]/result[@name='depts'][@numFound='" + deptMultiplier + "']/doc[1]/*)=1",
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[" + peopleMultiplier
                        + "]/result[@name='depts'][@numFound='" + deptMultiplier + "']/doc[" + deptMultiplier
                        + "]/*)=1"

        );
    }

    final String[] johnAndNancyParams = new String[] { "q", "name_s:(john nancy)", "indent", "true", "fl",
            "dept_ss_dv,name_s_dv,depts:[subquery]", "fl", "dept_i_dv,depts_i:[subquery]", "rows",
            "" + (2 * peopleMultiplier), "depts.q", "{!term f=dept_id_s v=$row.dept_ss_dv}", "depts.fl", "text_t",
            "depts.indent", "true", "depts.rows", "" + deptMultiplier,

            "depts_i.q", "{!term f=dept_id_i v=$row.dept_i_dv}", "depts_i.fl", "text_t", // multi val subquery param check
            "depts_i.fl", "dept_id_s_dv", "depts_i.indent", "true", "depts_i.rows", "" + deptMultiplier };

    @Test
    public void testTwoSubQueriesAndByNumberWithTwoFields() throws Exception {
        final SolrQueryRequest johnOrNancyTwoFL = req(johnAndNancyParams);

        assertQ("call subquery twice a row, once by number, with two fls via multival params", johnOrNancyTwoFL,
                "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts']/doc/str[@name='text_t'][.='These guys develop stuff'])="
                        + (peopleMultiplier * deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Engineering'])="
                        + (peopleMultiplier * deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='text_t'][.='These guys sell stuff'])="
                        + (peopleMultiplier * deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='nancy']/../result[@name='depts_i']/doc/str[@name='dept_id_s_dv'][.='Sales'])="
                        + (peopleMultiplier * deptMultiplier),
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[" + peopleMultiplier
                        + "]/result[@name='depts_i']/doc[" + deptMultiplier
                        + "]/str[@name='dept_id_s_dv'][.='Engineering'])=1",
                "count((//result/doc/str[@name='name_s_dv'][.='john']/..)[" + peopleMultiplier
                        + "]/result[@name='depts_i']/doc[" + deptMultiplier
                        + "]/str[@name='text_t'][.='These guys develop stuff'])=1");
    }

    @Test
    public void testRowsStartForSubqueryAndScores() throws Exception {

        String johnDeptsIds = h
                .query(req(new String[] { "q", "{!join from=dept_ss_dv to=dept_id_s}name_s:john", "wt", "csv",
                        "csv.header", "false", "fl", "id", "rows", "" + deptMultiplier, "sort", "id_i desc" }));

        ArrayList<Object> deptIds = Collections.list(new StringTokenizer(johnDeptsIds));

        final int a = random().nextInt(deptMultiplier + 1);
        final int b = random().nextInt(deptMultiplier + 1);
        final int start = Math.min(a, b);
        final int toIndex = Math.max(a, b);
        List<Object> expectIds = deptIds.subList(start, toIndex);
        ArrayList<String> assertions = new ArrayList<>();
        // count((//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc/str[@name='id'])
        // random().nextInt(peopleMultiplier);
        assertions.add("count((//result/doc/str[@name='name_s_dv'][.='john']/.."
                + "/result[@name='depts'][@numFound='" + deptMultiplier + "'][@start='" + start + "'])["
                + (random().nextInt(peopleMultiplier) + 1) + "]/doc/str[@name='id'])=" + (toIndex - start));

        // System.out.println(expectIds);

        for (int i = 0; i < expectIds.size(); i++) {
            // (//result/doc/str[@name='name_s_dv'][.='john']/../result[@name='depts'])[1]/doc[1]/str[@name='id']='15'
            String ithDoc = "(//result/doc/str[@name='name_s_dv'][.='john']/.."
                    + "/result[@name='depts'][@numFound='" + deptMultiplier + "'][@start='" + start + "'])["
                    + (random().nextInt(peopleMultiplier) + 1) + "]/doc[" + (i + 1) + "]";
            assertions.add(ithDoc + "/str[@name='id'][.='" + expectIds.get(i) + "']");
            // let's test scores right there
            assertions.add(ithDoc + "/float[@name='score'][.='" + expectIds.get(i) + ".0']");

        }

        String[] john = new String[] { "q", "name_s:john", "indent", "true", "fl",
                "dept_ss_dv,name_s_dv,depts:[subquery]", "rows", "" + (2 * peopleMultiplier), "depts.q",
                "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i", "depts.fl", "id", "depts.fl", "score",
                "depts.indent", "true", "depts.rows", "" + (toIndex - start), "depts.start", "" + start };

        assertQ(req(john), assertions.toArray(new String[] {}));
    }

    @Test
    public void testThreeLevel() throws Exception {
        List<String> asserts = new ArrayList<>();
        // dave works in both dept, get his coworkers from both
        for (String dept : new String[] { "Engineering", "Support" }) { //dept_id_s_dv">Engineering

            ArrayList<Object> deptWorkers = Collections.list(new StringTokenizer(h.query(req("q",
                    "dept_ss_dv:" + dept, //dept_id_i_dv
                    "wt", "csv", "csv.header", "false", "fl", "name_s_dv", "rows", "" + peopleMultiplier * 3, // dave has three coworkers in two depts
                    "sort", "id desc"))));
            // System.out.println(deptWorkers);

            // looping dave clones
            for (int p : new int[] { 1, peopleMultiplier }) {
                // looping dept clones
                for (int d : new int[] { 1, deptMultiplier }) {
                    // looping coworkers
                    int wPos = 1;
                    for (Object mate : deptWorkers) {
                        // (/response/result/doc/str[@name='name_s_dv'][.='dave']/..)[1]
                        //  /result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='Engineering']/..
                        //  /result[@name='neighbours']/doc/str[@name='name_s_dv'][.='tina']
                        asserts.add("((/response/result/doc/str[@name='name_s_dv'][.='dave']/..)[" + p + "]"
                                + "/result[@name='subq1']/doc/str[@name='dept_id_s_dv'][.='" + dept + "']/..)[" + d
                                + "]" + "/result[@name='neighbours']/doc[" + wPos + "]/str[@name='name_s_dv'][.='"
                                + mate + "']");
                        wPos++;
                    }

                }
            }
        }
        //System.out.println(asserts);
        assertQ("dave works at both dept with other folks",
                //  System.out.println(h.query( 
                req(new String[] { "q", "name_s:dave", "indent", "true", "fl",
                        "dept_ss_dv,name_s_dv,subq1:[subquery]", "rows", "" + peopleMultiplier, "subq1.q",
                        "{!terms f=dept_id_s v=$row.dept_ss_dv}", "subq1.fl",
                        "dept_id_i_dv,text_t,dept_id_s_dv,neighbours:[subquery]", "subq1.indent", "true",
                        "subq1.rows", "" + (deptMultiplier * 2), "subq1.neighbours.q", //flipping via numbers 
                        random().nextBoolean() ? "{!terms f=dept_ss_dv v=$row.dept_id_s_dv}"
                                : "{!terms f=dept_is v=$row.dept_id_i_dv}",
                        "subq1.neighbours.fl", "name_s_dv", "subq1.neighbours.rows", "" + peopleMultiplier * 3 },
                        "subq1.neighbours.sort", "id desc")//,
                , asserts.toArray(new String[] {})
        //        ) 
        );

    }

    @Test
    public void testNoExplicitName() throws Exception {
        String[] john = new String[] { "q", "name_s:john", "indent", "true", "fl", "name_s_dv," + "[subquery]",
                "rows", "" + (2 * peopleMultiplier), "depts.q",
                "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i", "depts.fl", "id", "depts.fl", "score",
                "depts.indent", "true", "depts.rows", "" + deptMultiplier, "depts.start", "0" };

        assertQEx("no prefix, no subquery", req(john), ErrorCode.BAD_REQUEST);

        assertQEx("no prefix, no subsubquery",
                req("q", "name_s:john", "indent", "true", "fl", "name_s_dv," + "depts:[subquery]", "rows",
                        "" + (2 * peopleMultiplier), "depts.q",
                        "+{!term f=dept_id_s v=$row.dept_ss_dv}^=0 _val_:id_i", "depts.fl", "id", "depts.fl",
                        "score", "depts.fl", "[subquery]", // <- here is a trouble
                        "depts.indent", "true", "depts.rows", "" + deptMultiplier, "depts.start", "0"),
                ErrorCode.BAD_REQUEST);
    }

    @Test
    public void testDupePrefix() throws Exception {
        assertQEx("subquery name clash",
                req(new String[] { "q", "name_s:(john nancy)", "indent", "true", "fl", "name_s_dv,depts:[subquery]",
                        "fl", "depts:[subquery]", "rows", "" + (2 * peopleMultiplier), "depts.q",
                        "{!term f=dept_id_s v=$row.dept_ss_dv}", "depts.fl", "text_t", "depts.indent", "true",
                        "depts.rows", "" + deptMultiplier,

                        "depts_i.q", "{!term f=dept_id_i v=$depts_i.row.dept_i_dv}", "depts_i.fl", "text_t", // multi val subquery param check
                        "depts_i.fl", "dept_id_s_dv", "depts_i.indent", "true", "depts_i.rows",
                        "" + deptMultiplier }),
                ErrorCode.BAD_REQUEST);
    }

    @Test
    public void testJustJohnJson() throws Exception {

        final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
        ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
        params.set("q", "name_s:john");
        johnTwoFL.setParams(params);
        assertJQ(johnTwoFL, "/response/docs/[0]/depts/docs/[0]=={text_t:\"These guys develop stuff\"}",
                "/response/docs/[" + (peopleMultiplier - 1) + "]/depts/docs/[" + (deptMultiplier - 1)
                        + "]=={text_t:\"These guys develop stuff\"}",

                "/response/docs/[0]/depts_i/docs/[0]=={dept_id_s_dv:\"Engineering\", text_t:\"These guys develop stuff\"}", // seem like key order doesn't matter , well
                "/response/docs/[" + (peopleMultiplier - 1) + "]/depts_i/docs/[" + (deptMultiplier - 1) + "]=="
                        + "{text_t:\"These guys develop stuff\", dept_id_s_dv:\"Engineering\"}");
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testJustJohnJavabin() throws Exception {
        final SolrQueryRequest johnTwoFL = req(johnAndNancyParams);
        ModifiableSolrParams params = new ModifiableSolrParams(johnTwoFL.getParams());
        params.set("q", "name_s:john");
        params.set("wt", "javabin");

        johnTwoFL.setParams(params);

        final NamedList<Object> unmarshalled;
        {
            SolrCore core = johnTwoFL.getCore();
            SolrQueryResponse rsp = new SolrQueryResponse();
            SolrRequestInfo.setRequestInfo(new SolrRequestInfo(johnTwoFL, rsp));

            SolrQueryResponse response = h.queryAndResponse(johnTwoFL.getParams().get(CommonParams.QT), johnTwoFL);

            BinaryQueryResponseWriter responseWriter = (BinaryQueryResponseWriter) core
                    .getQueryResponseWriter(johnTwoFL);
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            responseWriter.write(bytes, johnTwoFL, response);

            unmarshalled = (NamedList<Object>) new JavaBinCodec()
                    .unmarshal(new ByteArrayInputStream(bytes.toByteArray()));

            johnTwoFL.close();
            SolrRequestInfo.clearRequestInfo();
        }

        SolrDocumentList resultDocs = (SolrDocumentList) (unmarshalled.get("response"));

        {
            Map<String, String> engText = new HashMap<>();
            engText.put("text_t", "These guys develop stuff");

            Map<String, String> engId = new HashMap<>();
            engId.put("text_t", "These guys develop stuff");
            engId.put("dept_id_s_dv", "Engineering");

            for (int docNum : new int[] { 0, peopleMultiplier - 1 }) {
                SolrDocument employeeDoc = resultDocs.get(docNum);
                assertEquals("john", employeeDoc.getFieldValue("name_s_dv"));
                for (String subResult : new String[] { "depts", "depts_i" }) {

                    SolrDocumentList subDoc = (SolrDocumentList) employeeDoc.getFieldValue(subResult);
                    for (int deptNum : new int[] { 0, deptMultiplier - 1 }) {
                        SolrDocument deptDoc = subDoc.get(deptNum);
                        Object expectedDept = (subResult.equals("depts") ? engText : engId);
                        assertTrue("" + expectedDept + " equals to " + deptDoc, expectedDept.equals(deptDoc));
                    }
                }
            }
        }
    }

    @Test
    public void testExceptionPropagation() throws Exception {
        final SolrQueryRequest r = req("q", "name_s:dave", "indent", "true", "fl", "depts:[subquery]", "rows",
                "" + (peopleMultiplier), "depts.q", "{!lucene}(", "depts.fl", "text_t", "depts.indent", "true",
                "depts.rows", "" + (deptMultiplier * 2), "depts.logParamsList", "q,fl,rows,subq1.row.dept_ss_dv");

        // System.out.println(h.query(r));

        assertQEx("wrong subquery", r, ErrorCode.BAD_REQUEST);

        assertQEx("",
                req("q", "name_s:dave", "indent", "true", "fl", "depts:[subquery]", "rows", "1", "depts.q",
                        "{!lucene}", "depts.fl", "text_t", "depts.indent", "true", "depts.rows", "NAN",
                        "depts.logParamsList", "q,fl,rows,subq1.row.dept_ss_dv"),
                ErrorCode.BAD_REQUEST);
    }

    @Test
    public void testMultiValue() throws Exception {

        String[] happyPathAsserts = new String[] {
                "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys develop stuff'])="
                        + (peopleMultiplier * deptMultiplier),
                "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1']/doc/str[@name='text_t'][.='These guys help customers'])="
                        + (peopleMultiplier * deptMultiplier),
                "//result[@numFound=" + peopleMultiplier + "]" };
        Random random1 = random();

        assertQ("dave works at both, whether we set a  default separator or both",
                req(new String[] { "q", "name_s:dave", "indent", "true", "fl",
                        (random().nextBoolean() ? "name_s_dv,dept_ss_dv" : "*") + ",subq1:[subquery "
                                + ((random1.nextBoolean() ? "" : "separator=,")) + "]",
                        "rows", "" + peopleMultiplier, "subq1.q",
                        "{!terms f=dept_id_s v=$row.dept_ss_dv " + ((random1.nextBoolean() ? "" : "separator=,"))
                                + "}",
                        "subq1.fl", "text_t", "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2),
                        "subq1.logParamsList", "q,fl,rows,row.dept_ss_dv" }),
                happyPathAsserts);

        assertQ("even via numbers",
                req("q", "name_s:dave", "indent", "true", "fl", "dept_is_dv,name_s_dv,subq1:[subquery]", "rows",
                        "" + (peopleMultiplier), "subq1.q", "{!terms f=dept_id_i v=$row.dept_is_dv}", "subq1.fl",
                        "text_t", "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2)),
                happyPathAsserts);

        assertQ("even if we set a separator both", req("q", "name_s:dave", "indent", "true", "fl",
                "dept_ss_dv,name_s_dv,name_s_dv,subq1:[subquery separator=\" \"]", "rows", "" + (peopleMultiplier),
                "subq1.q", "{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}", "subq1.fl", "text_t",
                "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2)), happyPathAsserts);

        String[] noMatchAtSubQ = new String[] {
                "count(//result/doc/str[@name='name_s_dv'][.='dave']/../result[@name='subq1'][@numFound=0])="
                        + (peopleMultiplier),
                "//result[@numFound=" + peopleMultiplier + "]" };

        assertQ("different separators, no match",
                req("q", "name_s:dave", "indent", "true", "fl", "dept_ss_dv,name_s_dv,subq1:[subquery]", "rows",
                        "" + (peopleMultiplier), "subq1.q",
                        "{!terms f=dept_id_s v=$row.dept_ss_dv separator=\" \"}", "subq1.fl", "text_t",
                        "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2)),
                noMatchAtSubQ);

        assertQ("and no matter where",
                req("q", "name_s:dave", "indent", "true", "fl",
                        "dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]", "rows", "" + (peopleMultiplier),
                        "subq1.q", "{!terms f=dept_id_s v=$row.dept_ss_dv}", "subq1.fl", "text_t", "subq1.indent",
                        "true", "subq1.rows", "" + (deptMultiplier * 2)),
                noMatchAtSubQ);

        assertQ("setting a wrong parser gets you nowhere",
                req("q", "name_s:dave", "indent", "true", "fl", "dept_ss_dv,name_s_dv,subq1:[subquery]", "rows",
                        "" + (peopleMultiplier), "subq1.q", "{!term f=dept_id_s v=$row.dept_ss_dv}", "subq1.fl",
                        "text_t", "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2)),
                noMatchAtSubQ);

        assertQ("but it luckily works with default query parser, but it's not really reliable",
                req("q", "name_s:dave", "indent", "true", "fl",
                        "dept_ss_dv,name_s_dv,subq1:[subquery separator=\" \"]", "rows", "" + (peopleMultiplier),
                        "subq1.q", "{!lucene df=dept_id_s v=$row.dept_ss_dv}", "subq1.fl", "text_t", "subq1.indent",
                        "true", "subq1.rows", "" + (deptMultiplier * 2)),
                happyPathAsserts);

        assertQ("even lucene qp can't help at any separator but space",
                req("q", "name_s:dave", "indent", "true", "fl",
                        "dept_ss_dv,name_s_dv," + "subq1:[subquery "
                                + (random().nextBoolean() ? ""
                                        : "separator=" + ((random().nextBoolean() ? "" : ",")))
                                + "]",
                        "rows", "" + (peopleMultiplier), "subq1.q", "{!lucene df=dept_id_s v=$row.dept_ss_dv}",
                        "subq1.fl", "text_t", "subq1.indent", "true", "subq1.rows", "" + (deptMultiplier * 2)),
                noMatchAtSubQ);
    }

    static String[] daveMultiValueSearchParams(Random random, int peopleMult, int deptMult) {
        return new String[] { "q", "name_s:dave", "indent", "true", "fl",
                (random().nextBoolean() ? "name_s_dv" : "*") + //"dept_ss_dv,
                        ",subq1:[subquery " + ((random.nextBoolean() ? "" : "separator=,")) + "]",
                "rows", "" + peopleMult, "subq1.q",
                "{!terms f=dept_id_s v=$row.dept_ss_dv " + ((random.nextBoolean() ? "" : "separator=,")) + "}",
                "subq1.fl", "text_t", "subq1.indent", "true", "subq1.rows", "" + (deptMult * 2),
                "subq1.logParamsList", "q,fl,rows,row.dept_ss_dv" };
    }
}