TEST(QueryPlannerTest, createTripleGraph) {
  try {
    {
      ParsedQuery pq = SparqlParser::parse(
          "PREFIX : <http://rdf.myprefix.com/>\n"
          "PREFIX ns: <http://rdf.myprefix.com/ns/>\n"
          "PREFIX xxx: <http://rdf.myprefix.com/xxx/>\n"
          "SELECT ?x ?z \n "
          "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y xxx:rel2 "
          "<http://abc.de>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ASSERT_EQ(
          "0 {s: ?x, p: <http://rdf.myprefix.com/myrel>, o: ?y} : (1, 2)\n"
          "1 {s: ?y, p: <http://rdf.myprefix.com/ns/myrel>, o: ?z} : (0, 2)\n"
          "2 {s: ?y, p: <http://rdf.myprefix.com/xxx/rel2>, o: "
          "<http://abc.de>} : (0, 1)",
          tg.asString());
    }

    {
      ParsedQuery pq = SparqlParser::parse(
          "SELECT ?x WHERE {?x ?p <X>. ?x ?p2 <Y>. <X> ?p <Y>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ASSERT_EQ(
          "0 {s: ?x, p: ?p, o: <X>} : (1, 2)\n"
          "1 {s: ?x, p: ?p2, o: <Y>} : (0)\n"
          "2 {s: <X>, p: ?p, o: <Y>} : (0)",
          tg.asString());
    }

    {
      ParsedQuery pq = SparqlParser::parse(
          "SELECT ?x WHERE { ?x <is-a> <Book> . \n"
          "?x <Author> <Anthony_Newman_(Author)> }");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ASSERT_EQ(
          "0 {s: ?x, p: <is-a>, o: <Book>} : (1)\n"
          "1 {s: ?x, p: <Author>, o: <Anthony_Newman_(Author)>} : (0)",
          tg.asString());
    }
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryPlannerTest, testBFSLeaveOut) {
  try {
    {
      ParsedQuery pq = SparqlParser::parse(
          "SELECT ?x WHERE {?x ?p <X>. ?x ?p2 <Y>. <X> ?p <Y>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ASSERT_EQ(3u, tg._adjLists.size());
      ad_utility::HashSet<size_t> lo;
      auto out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(3u, out.size());
      lo.insert(1);
      out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(2u, out.size());
      lo.insert(2);
      out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(1u, out.size());
      lo.clear();
      lo.insert(0);
      out = tg.bfsLeaveOut(1, lo);
      ASSERT_EQ(1u, out.size());
    }
    {
      ParsedQuery pq = SparqlParser::parse(
          "SELECT ?x WHERE {<A> <B> ?x. ?x <C> ?y. ?y <X> <Y>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ad_utility::HashSet<size_t> lo;
      auto out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(3u, out.size());
      lo.insert(1);
      out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(1u, out.size());
      lo.insert(2);
      out = tg.bfsLeaveOut(0, lo);
      ASSERT_EQ(1u, out.size());
      lo.clear();
      lo.insert(0);
      out = tg.bfsLeaveOut(1, lo);
      ASSERT_EQ(2u, out.size());
    }
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testBornInEuropeOwCocaine) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "PREFIX : <>\n"
        "SELECT ?x ?y TEXT(?c)\n "
        "WHERE \t {"
        "?x :Place_of_birth ?y ."
        "?y :Contained_by :Europe ."
        "?c ql:contains-entity ?x ."
        "?c ql:contains-word \"cocaine\" ."
        "}");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  TEXT OPERATION WITH FILTER: co-occurrence with words: "
        "\"cocaine\" and 1 variables with textLimit = 1 filtered by\n  "
        "{\n    JOIN\n    {\n      SCAN POS with P = \"<Contained_by>\", "
        "O = \"<Europe>\"\n      qet-width: 1 \n    } join-column: [0]\n"
        "    |X|\n    {\n      SCAN POS with P = \"<Place_of_birth>\"\n"
        "      qet-width: 2 \n    } join-column: [0]\n    qet-width: 2 \n"
        "  }\n   filtered on column 1\n  qet-width: 4 \n}",
        qet.asString());
    ASSERT_EQ(0u, qet.getVariableColumn("?c"));
    ASSERT_EQ(1u, qet.getVariableColumn("SCORE(?c)"));
    ASSERT_EQ(2u, qet.getVariableColumn("?y"));
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testCoOccFreeVar) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "PREFIX : <>"
        "SELECT ?x ?y WHERE {"
        "?x :is-a :Politician ."
        "?c ql:contains-entity ?x ."
        "?c ql:contains-word \"friend*\" ."
        "?c ql:contains-entity ?y ."
        "}");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  TEXT OPERATION WITH FILTER: co-occurrence with words: "
        "\"friend*\" and 2 variables with textLimit = 1 filtered by\n"
        "  {\n    SCAN POS with P = \"<is-a>\", O = \"<Politician>"
        "\"\n    qet-width: 1 \n  }\n   filtered on column 0\n "
        " qet-width: 4 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryPlannerTest, testStarTwoFree) {
  try {
    {
      ParsedQuery pq = SparqlParser::parse(
          "PREFIX : <http://rdf.myprefix.com/>\n"
          "PREFIX ns: <http://rdf.myprefix.com/ns/>\n"
          "PREFIX xxx: <http://rdf.myprefix.com/xxx/>\n"
          "SELECT ?x ?z \n "
          "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z. ?y xxx:rel2 "
          "<http://abc.de>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      QueryExecutionTree qet = qp.createExecutionTree(pq);
      ASSERT_EQ(
          "{\n  JOIN\n  {\n    JOIN\n    {\n      SCAN POS with P = \""
          "<http://rdf.myprefix.com/myrel>\"\n      qet-width: 2 \n "
          "   } join-column: [0]\n    |X|\n    {\n      SCAN PSO wit"
          "h P = \"<http://rdf.myprefix.com/ns/myrel>\"\n      qet-"
          "width: 2 \n    } join-column: [0]\n    qet-width: 3 \n  "
          "} join-column: [0]\n  |X|\n  {\n    SCAN POS with P = "
          "\"<http://rdf.myprefix.com/xxx/rel2>\", O = \"<http://a"
          "bc.de>\"\n    qet-width: 1 \n  } join-column: [0]\n  qet"
          "-width: 3 \n}",
          qet.asString());
    }
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryPlannerTest, testActorsBornInEurope) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "PREFIX : <pre/>\n"
        "SELECT ?a \n "
        "WHERE {?a :profession :Actor . ?a :born-in ?c. ?c :in :Europe}\n"
        "ORDER BY ?a");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  JOIN\n  {\n    SCAN POS with P = \"<pre/profession>\", "
        "O = \"<pre/Actor>\"\n    qet-width: 1 \n  } join-column:"
        " [0]\n  |X|\n  {\n    SORT on column:1\n    {\n      "
        "JOIN\n      {\n        SCAN POS with P = \"<pre/born-i"
        "n>\"\n        qet-width: 2 \n      } join-column: [0]\n "
        "     |X|\n      {\n        SCAN POS with P = \"<pre/in>\""
        ", O = \"<pre/Europe>\"\n        qet-width: 1 \n      }"
        " join-column: [0]\n      qet-width: 2 \n    }\n    "
        "qet-width: 2 \n  } join-column: [1]\n  qet-width: 2 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testCyclicQuery) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?x ?y ?m WHERE { ?x <Spouse_(or_domestic_partner)> ?y . "
        "?x <Film_performance> ?m . ?y <Film_performance> ?m }");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  TWO_COLUMN_JOIN\n    {\n    ORDER_BY\n    {\n      JOIN\n"
        "      {\n        SCAN PSO with P = \"<Film_performance>\"\n"
        "        qet-width: 2 \n      } join-column: [0]\n      |X|\n"
        "      {\n        "
        "SCAN PSO with P = \"<Spouse_(or_domestic_partner)>\"\n    "
        "    qet-width: 2 \n      } join-column: [0]\n "
        "     qet-width: 3 \n    } order on asc(2) asc(1) \n"
        "    qet-width: 3 \n  }\n  join-columns: [2 & 1]\n  |X|\n"
        "    {\n    SCAN PSO with P = \"<Film_performance>\"\n"
        "    qet-width: 2 \n  }\n  join-columns: [0 & 1]\n"
        "  qet-width: 3 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testFormerSegfaultTriFilter) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "PREFIX fb: <http://rdf.freebase.com/ns/>\n"
        "SELECT DISTINCT ?1 ?0 WHERE {\n"
        "fb:m.0fkvn fb:government.government_office_category.officeholders ?0 "
        ".\n"
        "?0 fb:government.government_position_held.jurisdiction_of_office "
        "fb:m.0vmt .\n"
        "?0 fb:government.government_position_held.office_holder ?1 .\n"
        "FILTER (?1 != fb:m.0fkvn) .\n"
        "FILTER (?1 != fb:m.0vmt) .\n"
        "FILTER (?1 != fb:m.018mts)"
        "} LIMIT 300");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_TRUE(qet.varCovered("?1"));
    ASSERT_TRUE(qet.varCovered("?0"));
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testPlantsEdibleLeaves) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?a \n "
        "WHERE  {?a <is-a> <Plant> . ?c ql:contains-entity ?a. "
        "?c ql:contains-word \"edible leaves\"} TEXTLIMIT 5");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryPlanner::TripleGraph tg = qp.createTripleGraph(&pq._rootGraphPattern);
    ASSERT_EQ(1u, tg._nodeMap.find(0)->second->_variables.size());
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  TEXT OPERATION WITH FILTER: co-occurrence with words: "
        "\"edible leaves\" and 1 variables with textLimit = 5 "
        "filtered by\n  {\n    SCAN POS with P = \"<is-a>\", "
        "O = \"<Plant>\"\n    qet-width: 1 \n  }\n   filtered on "
        "column 0\n  qet-width: 3 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(ParserTest, testExpandPrefixes) {
  ParsedQuery pq = SparqlParser::parse(
      "PREFIX : <http://rdf.myprefix.com/>\n"
          "PREFIX ns: <http://rdf.myprefix.com/ns/>\n"
          "PREFIX xxx: <http://rdf.myprefix.com/xxx/>\n"
          "SELECT ?x ?z \n "
          "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y nsx:rel2 <http://abc.de>}");
  pq.expandPrefixes();
  ASSERT_EQ(3, pq._prefixes.size());
  ASSERT_EQ(2, pq._selectedVariables.size());
  ASSERT_EQ(3, pq._whereClauseTriples.size());
  ASSERT_EQ("", pq._prefixes[0]._prefix);
  ASSERT_EQ("<http://rdf.myprefix.com/>", pq._prefixes[0]._uri);
  ASSERT_EQ("ns", pq._prefixes[1]._prefix);
  ASSERT_EQ("<http://rdf.myprefix.com/ns/>", pq._prefixes[1]._uri);
  ASSERT_EQ("?x", pq._selectedVariables[0]);
  ASSERT_EQ("?z", pq._selectedVariables[1]);
  ASSERT_EQ("?x", pq._whereClauseTriples[0]._s);
  ASSERT_EQ("<http://rdf.myprefix.com/myrel>", pq._whereClauseTriples[0]._p);
  ASSERT_EQ("?y", pq._whereClauseTriples[0]._o);
  ASSERT_EQ("?y", pq._whereClauseTriples[1]._s);
  ASSERT_EQ("<http://rdf.myprefix.com/ns/myrel>", pq._whereClauseTriples[1]
      ._p);
  ASSERT_EQ("?z", pq._whereClauseTriples[1]._o);
  ASSERT_EQ("?y", pq._whereClauseTriples[2]._s);
  ASSERT_EQ("nsx:rel2", pq._whereClauseTriples[2]._p);
  ASSERT_EQ("<http://abc.de>", pq._whereClauseTriples[2]._o);
  ASSERT_EQ("", pq._limit);
  ASSERT_EQ("", pq._offset);
}
TEST(QueryPlannerTest, testSimpleOptional) {
  try {
    QueryPlanner qp(nullptr);

    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?a ?b \n "
        "WHERE  {?a <rel1> ?b . OPTIONAL { ?a <rel2> ?c }}");
    pq.expandPrefixes();
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n"
        "  OPTIONAL_JOIN\n"
        "  {\n"
        "    SCAN PSO with P = \"<rel1>\"\n"
        "    qet-width: 2 \n"
        "  } join-columns: [0]\n"
        "  |X|\n"
        "  {\n"
        "    SCAN PSO with P = \"<rel2>\"\n"
        "    qet-width: 2 \n"
        "  } join-columns: [0]\n"
        "  qet-width: 3 \n"
        "}",
        qet.asString());

    ParsedQuery pq2 = SparqlParser::parse(
        "SELECT ?a ?b \n "
        "WHERE  {?a <rel1> ?b . "
        "OPTIONAL { ?a <rel2> ?c }} ORDER BY ?b");
    pq2.expandPrefixes();
    QueryExecutionTree qet2 = qp.createExecutionTree(pq2);
    ASSERT_EQ(
        "{\n"
        "  SORT on column:1\n"
        "  {\n"
        "    OPTIONAL_JOIN\n"
        "    {\n"
        "      SCAN PSO with P = \"<rel1>\"\n"
        "      qet-width: 2 \n"
        "    } join-columns: [0]\n"
        "    |X|\n"
        "    {\n"
        "      SCAN PSO with P = \"<rel2>\"\n"
        "      qet-width: 2 \n"
        "    } join-columns: [0]\n"
        "    qet-width: 3 \n"
        "  }\n"
        "  qet-width: 3 \n"
        "}",
        qet2.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
Пример #12
0
// _____________________________________________________________________________
void Server::process(Socket *client, QueryExecutionContext *qec) const {
  string request;
  string response;
  string query;
  string contentType;
  client->recieve(&request);

  size_t indexOfGET = request.find("GET");
  size_t indexOfHTTP = request.find("HTTP");
  size_t upper = indexOfHTTP;

  if (indexOfGET != request.npos && indexOfHTTP != request.npos) {
    size_t indexOfQuest = request.find("?", indexOfGET);
    if (indexOfQuest != string::npos && indexOfQuest < indexOfHTTP) {
      upper = indexOfQuest + 1;
    }
    string file = request.substr(indexOfGET + 5, upper - (indexOfGET + 5) - 1);
    // Use hardcoded white-listing for index.html and style.css
    // can be changed if more should ever be needed, for now keep it simple.
    LOG(DEBUG) << "file: " << file << '\n';
    if (file == "index.html" || file == "style.css" || file == "script.js") {
      serveFile(client, file);
      return;
    }
    if (indexOfQuest == string::npos) {
      LOG(INFO) << "Ignoring request for file " << file << '\n';
      return;
    }

    try {
      ParamValueMap params = parseHttpRequest(request);
      if (ad_utility::getLowercase(params["cmd"]) == "clearcache") {
        qec->clearCache();
      }
      query = createQueryFromHttpParams(params);
      LOG(INFO) << "Query: " << query << '\n';
      ParsedQuery pq = SparqlParser::parse(query);
      pq.expandPrefixes();

      QueryGraph qg(qec);
      qg.createFromParsedQuery(pq);
      const QueryExecutionTree& qet = qg.getExecutionTree();
      response = composeResponseJson(pq, qet);
      contentType = "application/json";
    } catch (const ad_semsearch::Exception& e) {
      response = composeResponseJson(query, e);
    } catch (const ParseException& e) {
      response = composeResponseJson(query, e);
    }
    string httpResponse = createHttpResponse(response, contentType);
    client->send(httpResponse);
  } else {
    LOG(INFO) << "Ignoring invalid request " << request << '\n';
  }
}
TEST(QueryPlannerTest, testSP_free_) {
  ParsedQuery pq = SparqlParser::parse(
      "PREFIX : <http://rdf.myprefix.com/>\n"
      "SELECT ?x \n "
      "WHERE \t {?x :myrel ?y}");
  pq.expandPrefixes();
  QueryPlanner qp(nullptr);
  QueryExecutionTree qet = qp.createExecutionTree(pq);
  ASSERT_EQ(
      "{\n  SCAN PSO with P = \"<http://rdf.myprefix.com/myrel>\"\n  "
      "qet-width: 2 \n}",
      qet.asString());
}
TEST(QueryExecutionTreeTest, testBooksGermanAwardNomAuth) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?x ?y WHERE { "
        "?x <is-a> <Person> . "
        "?x <Country_of_nationality> <Germany> . "
        "?x <Author> ?y . "
        "?y <is-a> <Award-Nominated_Work> }");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_GT(qet.asString().size(), 0u);
    // Just check that ther is no exception, here.
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testTextQuerySE) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT TEXT(?c) \n "
        "WHERE  {?c ql:contains-word \"search engine\"}");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  TEXT OPERATION WITHOUT FILTER: co-occurrence with words:"
        " \"search engine\" and 0 variables with textLimit = 1\n"
        "  qet-width: 2 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryPlannerTest, test_free_PX__free_PX) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "PREFIX : <pre/>\n"
        "SELECT ?x ?y ?z \n "
        "WHERE {?y :r ?x. ?z :r ?x}");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  JOIN\n  {\n    SCAN POS with P = \"<pre/r>\"\n    "
        "qet-width: 2 \n  } join-column: [0]\n  |X|\n  {\n    "
        "SCAN POS with P = \"<pre/r>\"\n    qet-width: 2 \n"
        "  } join-column: [0]\n  qet-width: 3 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testBooksbyNewman) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?x WHERE { ?x <is-a> <Book> . "
        "?x <Author> <Anthony_Newman_(Author)> }");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  JOIN\n  {\n    SCAN POS with "
        "P = \"<Author>\", O = \"<Anthony_Newman_(Author)>\"\n   "
        " qet-width: 1 \n  } join-column: [0]\n  |X|\n  {\n  "
        "  SCAN POS with P = \"<is-a>\", O = \"<Book>\"\n  "
        "  qet-width: 1 \n  } join-column: [0]\n  qet-width: 1 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryExecutionTreeTest, testPoliticiansFriendWithScieManHatProj) {
  try {
    ParsedQuery pq = SparqlParser::parse(
        "SELECT ?p ?s \n "
        "WHERE {"
        "?a <is-a> <Politician> . "
        "?c ql:contains-entity ?a ."
        "?c ql:contains-word \"friend*\" ."
        "?c ql:contains-entity ?s ."
        "?s <is-a> <Scientist> ."
        "?c2 ql:contains-entity ?s ."
        "?c2 ql:contains-word \"manhattan project\"}");
    pq.expandPrefixes();
    QueryPlanner qp(nullptr);
    QueryExecutionTree qet = qp.createExecutionTree(pq);
    ASSERT_EQ(
        "{\n  JOIN\n  {\n    SCAN POS with P = \"<is-a>\", O = "
        "\"<Scientist>\"\n    qet-width: 1 \n  } join-column: [0]\n  |X|\n  "
        "{\n    SORT on column:4\n    {\n      TEXT OPERATION WITH FILTER: "
        "co-occurrence with words: \"manhattan project\" and 1 variables with "
        "textLimit = 1 filtered by\n      {\n        TEXT OPERATION WITH "
        "FILTER: co-occurrence with words: \"friend*\" and 2 variables with "
        "textLimit = 1 filtered by\n        {\n          SCAN POS with P = "
        "\"<is-a>\", O = \"<Politician>\"\n          qet-width: 1 \n        "
        "}\n         filtered on column 0\n        qet-width: 4 \n      }\n    "
        "   filtered on column 2\n      qet-width: 6 \n    }\n    qet-width: 6 "
        "\n  } join-column: [4]\n  qet-width: 6 \n}",
        qet.asString());
  } catch (const ad_semsearch::Exception& e) {
    std::cout << "Caught: " << e.getFullErrorMessage() << std::endl;
    FAIL() << e.getFullErrorMessage();
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
// Main function.
int main(int argc, char** argv) {
  cout.sync_with_stdio(false);
  std::cout << std::endl
            << EMPH_ON << "WriteIndexListsMain, version " << __DATE__ << " "
            << __TIME__ << EMPH_OFF << std::endl
            << std::endl;

  char* locale = setlocale(LC_CTYPE, "");
  cout << "Set locale LC_CTYPE to: " << locale << endl;

  std::locale loc;
  ad_utility::ReadableNumberFacet facet(1);
  std::locale locWithNumberGrouping(loc, &facet);
  ad_utility::Log::imbue(locWithNumberGrouping);

  string indexName = "";
  bool freebase = false;

  optind = 1;
  // Process command line arguments.
  while (true) {
    int c = getopt_long(argc, argv, "i:f", options, NULL);
    if (c == -1) break;
    switch (c) {
      case 'i':
        indexName = optarg;
        break;
      case 'f':
        freebase = true;
        break;
      default:
        cout << endl
             << "! ERROR in processing options (getopt returned '" << c
             << "' = 0x" << std::setbase(16) << c << ")" << endl
             << endl;
        exit(1);
    }
  }

  if (indexName.size() == 0) {
    cout << "Missing required argument --index (-i)..." << endl;
    exit(1);
  }

  try {
    Index index;
    index.createFromOnDiskIndex(indexName);
    index.addTextFromOnDiskIndex();

    vector<string> lists;
    lists.push_back("algo*");
    bool decodeGapsAndFrequency = true;
    index.dumpAsciiLists(lists, decodeGapsAndFrequency);

    Engine engine;
    QueryExecutionContext qec(index, engine);
    ParsedQuery q;
    if (!freebase) {
      q = SparqlParser::parse("SELECT ?x WHERE {?x <is-a> <Scientist>}");
    } else {
      q = SparqlParser::parse(
          "PREFIX fb: <http://rdf.freebase.com/ns/> SELECT ?p WHERE {?p "
          "fb:people.person.profession fb:m.06q2q}");
      q.expandPrefixes();
    }
    QueryPlanner queryPlanner(&qec);
    auto qet = queryPlanner.createExecutionTree(q);
    const auto res = qet.getResult();
    AD_CHECK(res->size() > 0);
    AD_CHECK(res->_data.cols() == 1);
    string personlistFile = indexName + ".list.scientists";
    std::ofstream f(personlistFile.c_str());
    const IdTable& ids = res->_data;
    for (size_t i = 0; i < ids.size(); ++i) {
      f << ids(i, 0) << ' ';
    }
    f.close();

  } catch (const std::exception& e) {
    cout << e.what() << std::endl;
  }

  return 0;
}
TEST(ParserTest, testParse) {
  try {
    ParsedQuery pq = SparqlParser::parse("SELECT ?x WHERE {?x ?y ?z}");
    ASSERT_GT(pq.asString().size(), 0);
    ASSERT_EQ(0, pq._prefixes.size());
    ASSERT_EQ(1, pq._selectedVariables.size());
    ASSERT_EQ(1, pq._whereClauseTriples.size());

    pq = SparqlParser::parse(
        "PREFIX : <http://rdf.myprefix.com/>\n"
            "PREFIX ns: <http://rdf.myprefix.com/ns/>\n"
            "PREFIX xxx: <http://rdf.myprefix.com/xxx/>\n"
            "SELECT ?x ?z \n "
            "WHERE \t {?x :myrel ?y. ?y ns:myrel ?z.?y nsx:rel2 <http://abc.de>}");
    ASSERT_EQ(3, pq._prefixes.size());
    ASSERT_EQ(2, pq._selectedVariables.size());
    ASSERT_EQ(3, pq._whereClauseTriples.size());

    ASSERT_EQ("", pq._prefixes[0]._prefix);
    ASSERT_EQ("<http://rdf.myprefix.com/>", pq._prefixes[0]._uri);
    ASSERT_EQ("ns", pq._prefixes[1]._prefix);
    ASSERT_EQ("<http://rdf.myprefix.com/ns/>", pq._prefixes[1]._uri);
    ASSERT_EQ("?x", pq._selectedVariables[0]);
    ASSERT_EQ("?z", pq._selectedVariables[1]);
    ASSERT_EQ("?x", pq._whereClauseTriples[0]._s);
    ASSERT_EQ(":myrel", pq._whereClauseTriples[0]._p);
    ASSERT_EQ("?y", pq._whereClauseTriples[0]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[1]._s);
    ASSERT_EQ("ns:myrel", pq._whereClauseTriples[1]._p);
    ASSERT_EQ("?z", pq._whereClauseTriples[1]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[2]._s);
    ASSERT_EQ("nsx:rel2", pq._whereClauseTriples[2]._p);
    ASSERT_EQ("<http://abc.de>", pq._whereClauseTriples[2]._o);
    ASSERT_EQ("", pq._limit);
    ASSERT_EQ("", pq._offset);

    pq = SparqlParser::parse(
        "PREFIX : <http://rdf.myprefix.com/>\n"
            "PREFIX ns: <http://rdf.myprefix.com/ns/>\n"
            "PREFIX xxx: <http://rdf.myprefix.com/xxx/>\n"
            "SELECT ?x ?z \n "
            "WHERE \t {\n?x :myrel ?y. ?y ns:myrel ?z.\n?y nsx:rel2 <http://abc.de>\n}");
    ASSERT_EQ(3, pq._prefixes.size());
    ASSERT_EQ(2, pq._selectedVariables.size());
    ASSERT_EQ(3, pq._whereClauseTriples.size());

    ASSERT_EQ("", pq._prefixes[0]._prefix);
    ASSERT_EQ("<http://rdf.myprefix.com/>", pq._prefixes[0]._uri);
    ASSERT_EQ("ns", pq._prefixes[1]._prefix);
    ASSERT_EQ("<http://rdf.myprefix.com/ns/>", pq._prefixes[1]._uri);
    ASSERT_EQ("?x", pq._selectedVariables[0]);
    ASSERT_EQ("?z", pq._selectedVariables[1]);
    ASSERT_EQ("?x", pq._whereClauseTriples[0]._s);
    ASSERT_EQ(":myrel", pq._whereClauseTriples[0]._p);
    ASSERT_EQ("?y", pq._whereClauseTriples[0]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[1]._s);
    ASSERT_EQ("ns:myrel", pq._whereClauseTriples[1]._p);
    ASSERT_EQ("?z", pq._whereClauseTriples[1]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[2]._s);
    ASSERT_EQ("nsx:rel2", pq._whereClauseTriples[2]._p);
    ASSERT_EQ("<http://abc.de>", pq._whereClauseTriples[2]._o);
    ASSERT_EQ("", pq._limit);
    ASSERT_EQ("", pq._offset);

    pq = SparqlParser::parse(
        "PREFIX ns: <http://ns/>"
            "SELECT ?x ?z \n "
            "WHERE \t {\n?x <Directed_by> ?y. ?y ns:myrel.extend ?z.\n"
            "?y nsx:rel2 \"Hello... World\"}");
    ASSERT_EQ(1, pq._prefixes.size());
    ASSERT_EQ(2, pq._selectedVariables.size());
    ASSERT_EQ(3, pq._whereClauseTriples.size());

    pq.expandPrefixes();

    ASSERT_EQ("?x", pq._selectedVariables[0]);
    ASSERT_EQ("?z", pq._selectedVariables[1]);
    ASSERT_EQ("?x", pq._whereClauseTriples[0]._s);
    ASSERT_EQ("<Directed_by>", pq._whereClauseTriples[0]._p);
    ASSERT_EQ("?y", pq._whereClauseTriples[0]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[1]._s);
    ASSERT_EQ("<http://ns/myrel.extend>", pq._whereClauseTriples[1]._p);
    ASSERT_EQ("?z", pq._whereClauseTriples[1]._o);
    ASSERT_EQ("?y", pq._whereClauseTriples[2]._s);
    ASSERT_EQ("nsx:rel2", pq._whereClauseTriples[2]._p);
    ASSERT_EQ("\"Hello... World\"", pq._whereClauseTriples[2]._o);
    ASSERT_EQ("", pq._limit);
    ASSERT_EQ("", pq._offset);


    pq = SparqlParser::parse(
        "SELECT ?x ?y WHERE {?x is-a Actor .  FILTER(?x != ?y)."
            "?y is-a Actor . FILTER(?y < ?x)} LIMIT 10");
    pq.expandPrefixes();
    ASSERT_EQ(2, pq._filters.size());
    ASSERT_EQ("?x", pq._filters[0]._lhs);
    ASSERT_EQ("?y", pq._filters[0]._rhs);
    ASSERT_EQ(SparqlFilter::FilterType::NE, pq._filters[0]._type);
    ASSERT_EQ("?y", pq._filters[1]._lhs);
    ASSERT_EQ("?x", pq._filters[1]._rhs);
    ASSERT_EQ(SparqlFilter::FilterType::LT, pq._filters[1]._type);
    ASSERT_EQ(2, pq._whereClauseTriples.size());

    pq = SparqlParser::parse(
        "SELECT ?x ?y WHERE {?x is-a Actor .  FILTER(?x != ?y)."
            "?y is-a Actor} LIMIT 10");
    pq.expandPrefixes();
    ASSERT_EQ(1, pq._filters.size());
    ASSERT_EQ("?x", pq._filters[0]._lhs);
    ASSERT_EQ("?y", pq._filters[0]._rhs);
    ASSERT_EQ(SparqlFilter::FilterType::NE, pq._filters[0]._type);
    ASSERT_EQ(2, pq._whereClauseTriples.size());


    pq = SparqlParser::parse(
        "SELECT ?x ?y WHERE {?x is-a Actor .  FILTER(?x != ?y)."
            "?y is-a Actor. ?x <in-context> ?c."
            "?c <in-context> coca* abuse} LIMIT 10");
    pq.expandPrefixes();
    ASSERT_EQ(1, pq._filters.size());
    ASSERT_EQ("?x", pq._filters[0]._lhs);
    ASSERT_EQ("?y", pq._filters[0]._rhs);
    ASSERT_EQ(SparqlFilter::FilterType::NE, pq._filters[0]._type);
    ASSERT_EQ(4, pq._whereClauseTriples.size());
    ASSERT_EQ("?x", pq._whereClauseTriples[2]._s);
    ASSERT_EQ("<in-context>", pq._whereClauseTriples[2]._p);
    ASSERT_EQ("?c", pq._whereClauseTriples[2]._o);
    ASSERT_EQ("?c", pq._whereClauseTriples[3]._s);
    ASSERT_EQ("<in-context>", pq._whereClauseTriples[3]._p);
    ASSERT_EQ("coca* abuse", pq._whereClauseTriples[3]._o);

    pq = SparqlParser::parse(
        "PREFIX : <>\n"
            "SELECT ?x ?y ?z TEXT(?c) SCORE(?c) ?c WHERE {\n"
            "?x :is-a :Politician .\n"
            "?x :in-context ?c .\n"
            "?c :in-context friend .\n"
            "?c :in-context ?y .\n"
            "?y :is-a :Scientist .\n"
            "FILTER(?x != ?y) .\n"
            "} ORDER BY ?c");
    pq.expandPrefixes();
    ASSERT_EQ(1, pq._filters.size());
  }
  catch (const ad_semsearch::Exception& e) {
    FAIL() << e.getFullErrorMessage();
  }

};
TEST(QueryPlannerTest, testcollapseTextCliques) {
  try {
    {
      {
        ParsedQuery pq = SparqlParser::parse(
            "SELECT ?x WHERE {?x <p> <X>. ?c ql:contains-entity ?x. ?c "
            "ql:contains-word abc}");
        pq.expandPrefixes();
        QueryPlanner qp(nullptr);
        auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
        ASSERT_EQ(
            "0 {s: ?x, p: <p>, o: <X>} : (1)\n"
            "1 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?x} : "
            "(0, 2)\n"
            "2 {s: ?c, p: <QLever-internal-function/contains-word>, o: abc} : "
            "(1)",
            tg.asString());
        tg.collapseTextCliques();
        ASSERT_EQ(
            "0 {TextOP for ?c, wordPart: \"abc\"} : (1)\n"
            "1 {s: ?x, p: <p>, o: <X>} : (0)",
            tg.asString());
        ASSERT_EQ(2ul, tg._nodeMap[0]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[1]->_variables.size());
      }
      {
        ParsedQuery pq = SparqlParser::parse(
            "SELECT ?x WHERE {?x <p> <X>. ?c "
            "<QLever-internal-function/contains-entity> ?x. ?c "
            "<QLever-internal-function/contains-word> abc . ?c "
            "ql:contains-entity ?y}");
        pq.expandPrefixes();
        QueryPlanner qp(nullptr);
        auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
        ASSERT_EQ(
            "0 {s: ?x, p: <p>, o: <X>} : (1)\n"
            "1 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?x} : "
            "(0, 2, 3)\n"
            "2 {s: ?c, p: <QLever-internal-function/contains-word>, o: abc} : "
            "(1, 3)\n"
            "3 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?y} : "
            "(1, 2)",
            tg.asString());
        tg.collapseTextCliques();
        ASSERT_EQ(
            "0 {TextOP for ?c, wordPart: \"abc\"} : (1)\n"
            "1 {s: ?x, p: <p>, o: <X>} : (0)",
            tg.asString());
        ASSERT_EQ(3ul, tg._nodeMap[0]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[1]->_variables.size());
      }
      {
        ParsedQuery pq = SparqlParser::parse(
            "SELECT ?x WHERE {?x <p> <X>. ?c ql:contains-entity ?x. ?c "
            "ql:contains-word abc . ?c ql:contains-entity ?y. ?y <P2> <X2>}");
        pq.expandPrefixes();
        QueryPlanner qp(nullptr);
        auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
        ASSERT_EQ(
            "0 {s: ?x, p: <p>, o: <X>} : (1)\n"
            "1 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?x} : "
            "(0, 2, 3)\n"
            "2 {s: ?c, p: <QLever-internal-function/contains-word>, o: abc} : "
            "(1, 3)\n"
            "3 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?y} : "
            "(1, 2, 4)\n"
            "4 {s: ?y, p: <P2>, o: <X2>} : (3)",
            tg.asString());
        tg.collapseTextCliques();
        ASSERT_EQ(
            "0 {TextOP for ?c, wordPart: \"abc\"} : (1, 2)\n"
            "1 {s: ?x, p: <p>, o: <X>} : (0)\n"
            "2 {s: ?y, p: <P2>, o: <X2>} : (0)",
            tg.asString());
        ASSERT_EQ(3ul, tg._nodeMap[0]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[1]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[2]->_variables.size());
      }
      {
        ParsedQuery pq = SparqlParser::parse(
            "(SELECT ?x WHERE {?x <p> <X>. ?c ql:contains-entity ?x. ?c "
            "ql:contains-word \"abc\" . ?c ql:contains-entity ?y. ?c2 "
            "ql:contains-entity ?y. ?c2 ql:contains-word \"xx\"})");
        pq.expandPrefixes();
        QueryPlanner qp(nullptr);
        auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
        ASSERT_EQ(
            "0 {s: ?x, p: <p>, o: <X>} : (1)\n"
            "1 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?x} : "
            "(0, 2, 3)\n"
            "2 {s: ?c, p: <QLever-internal-function/contains-word>, o: abc} : "
            "(1, 3)\n"
            "3 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?y} : "
            "(1, 2, 4)\n"
            "4 {s: ?c2, p: <QLever-internal-function/contains-entity>, o: ?y} "
            ": (3, 5)\n"
            "5 {s: ?c2, p: <QLever-internal-function/contains-word>, o: xx} : "
            "(4)",
            tg.asString());
        tg.collapseTextCliques();
        ASSERT_EQ(
            "0 {TextOP for ?c, wordPart: \"abc\"} : (1, 2)\n"
            "1 {TextOP for ?c2, wordPart: \"xx\"} : (0)\n"
            "2 {s: ?x, p: <p>, o: <X>} : (0)",
            tg.asString());
        ASSERT_EQ(3ul, tg._nodeMap[0]->_variables.size());
        ASSERT_EQ(2ul, tg._nodeMap[1]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[2]->_variables.size());
      }
      {
        ParsedQuery pq = SparqlParser::parse(
            "SELECT ?x WHERE {?x <p> <X>. ?c ql:contains-entity ?x. ?c "
            "ql:contains-word abc . ?c ql:contains-entity ?y. ?c2 "
            "ql:contains-entity ?y. ?c2 ql:contains-word xx. ?y <P2> <X2>}");
        pq.expandPrefixes();
        QueryPlanner qp(nullptr);
        auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
        ASSERT_EQ(
            "0 {s: ?x, p: <p>, o: <X>} : (1)\n"
            "1 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?x} : "
            "(0, 2, 3)\n"
            "2 {s: ?c, p: <QLever-internal-function/contains-word>, o: abc} : "
            "(1, 3)\n"
            "3 {s: ?c, p: <QLever-internal-function/contains-entity>, o: ?y} : "
            "(1, 2, 4, 6)\n"
            "4 {s: ?c2, p: <QLever-internal-function/contains-entity>, o: ?y} "
            ": (3, 5, 6)\n"
            "5 {s: ?c2, p: <QLever-internal-function/contains-word>, o: xx} : "
            "(4)\n"
            "6 {s: ?y, p: <P2>, o: <X2>} : (3, 4)",
            tg.asString());
        tg.collapseTextCliques();
        ASSERT_EQ(
            "0 {TextOP for ?c, wordPart: \"abc\"} : (1, 2, 3)\n"
            "1 {TextOP for ?c2, wordPart: \"xx\"} : (0, 3)\n"
            "2 {s: ?x, p: <p>, o: <X>} : (0)\n"
            "3 {s: ?y, p: <P2>, o: <X2>} : (0, 1)",
            tg.asString());
        ASSERT_EQ(3ul, tg._nodeMap[0]->_variables.size());
        ASSERT_EQ(2ul, tg._nodeMap[1]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[2]->_variables.size());
        ASSERT_EQ(1ul, tg._nodeMap[3]->_variables.size());
      }
    }
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(QueryPlannerTest, testCpyCtorWithKeepNodes) {
  try {
    {
      ParsedQuery pq = SparqlParser::parse(
          "SELECT ?x WHERE {?x ?p <X>. ?x ?p2 <Y>. <X> ?p <Y>}");
      pq.expandPrefixes();
      QueryPlanner qp(nullptr);
      auto tg = qp.createTripleGraph(&pq._rootGraphPattern);
      ASSERT_EQ(2u, tg._nodeMap.find(0)->second->_variables.size());
      ASSERT_EQ(2u, tg._nodeMap.find(1)->second->_variables.size());
      ASSERT_EQ(1u, tg._nodeMap.find(2)->second->_variables.size());
      ASSERT_EQ(
          "0 {s: ?x, p: ?p, o: <X>} : (1, 2)\n"
          "1 {s: ?x, p: ?p2, o: <Y>} : (0)\n"
          "2 {s: <X>, p: ?p, o: <Y>} : (0)",
          tg.asString());
      {
        vector<size_t> keep;
        QueryPlanner::TripleGraph tgnew(tg, keep);
        ASSERT_EQ("", tgnew.asString());
      }
      {
        vector<size_t> keep;
        keep.push_back(0);
        keep.push_back(1);
        keep.push_back(2);
        QueryPlanner::TripleGraph tgnew(tg, keep);
        ASSERT_EQ(
            "0 {s: ?x, p: ?p, o: <X>} : (1, 2)\n"
            "1 {s: ?x, p: ?p2, o: <Y>} : (0)\n"
            "2 {s: <X>, p: ?p, o: <Y>} : (0)",
            tgnew.asString());
        ASSERT_EQ(2u, tgnew._nodeMap.find(0)->second->_variables.size());
        ASSERT_EQ(2u, tgnew._nodeMap.find(1)->second->_variables.size());
        ASSERT_EQ(1u, tgnew._nodeMap.find(2)->second->_variables.size());
      }
      {
        vector<size_t> keep;
        keep.push_back(0);
        QueryPlanner::TripleGraph tgnew(tg, keep);
        ASSERT_EQ("0 {s: ?x, p: ?p, o: <X>} : ()", tgnew.asString());
        ASSERT_EQ(2u, tgnew._nodeMap.find(0)->second->_variables.size());
      }
      {
        vector<size_t> keep;
        keep.push_back(0);
        keep.push_back(1);
        QueryPlanner::TripleGraph tgnew(tg, keep);
        ASSERT_EQ(
            "0 {s: ?x, p: ?p, o: <X>} : (1)\n"
            "1 {s: ?x, p: ?p2, o: <Y>} : (0)",
            tgnew.asString());
        ASSERT_EQ(2u, tgnew._nodeMap.find(0)->second->_variables.size());
        ASSERT_EQ(2u, tgnew._nodeMap.find(1)->second->_variables.size());
      }
    }
  } catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
    FAIL() << e.what();
  }
}
TEST(ParserTest, testSolutionModifiers) {
  ParsedQuery pq = SparqlParser::parse(
      "SELECT ?x WHERE \t {?x :myrel ?y}");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(1, pq._selectedVariables.size());
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("", pq._limit);
  ASSERT_EQ("", pq._offset);
  ASSERT_EQ(size_t(0), pq._orderBy.size());
  ASSERT_FALSE(pq._distinct);
  ASSERT_FALSE(pq._reduced);


  pq = SparqlParser::parse(
      "SELECT ?x WHERE \t {?x :myrel ?y} LIMIT 10");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(1, pq._selectedVariables.size());
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("10", pq._limit);
  ASSERT_EQ("", pq._offset);
  ASSERT_EQ(size_t(0), pq._orderBy.size());
  ASSERT_FALSE(pq._distinct);
  ASSERT_FALSE(pq._reduced);

  pq = SparqlParser::parse(
      "SELECT ?x WHERE \t {?x :myrel ?y}\n"
          "LIMIT 10 OFFSET 15");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(1, pq._selectedVariables.size());
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("10", pq._limit);
  ASSERT_EQ("15", pq._offset);
  ASSERT_EQ(size_t(0), pq._orderBy.size());
  ASSERT_FALSE(pq._distinct);
  ASSERT_FALSE(pq._reduced);

  pq = SparqlParser::parse(
      "SELECT DISTINCT ?x ?y WHERE \t {?x :myrel ?y}\n"
          "ORDER BY ?y LIMIT 10 OFFSET 15");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(2, pq._selectedVariables.size());
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("10", pq._limit);
  ASSERT_EQ("15", pq._offset);
  ASSERT_EQ(size_t(1), pq._orderBy.size());
  ASSERT_EQ("?y", pq._orderBy[0]._key);
  ASSERT_FALSE(pq._orderBy[0]._desc);
  ASSERT_TRUE(pq._distinct);
  ASSERT_FALSE(pq._reduced);

  pq = SparqlParser::parse(
      "SELECT DISTINCT ?x SCORE(?x|?c) ?y WHERE \t {?x :myrel ?y}\n"
          "ORDER BY ASC(?y) DESC(SCORE(?x|?c)) LIMIT 10 OFFSET 15");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(3, pq._selectedVariables.size());
  ASSERT_EQ("SCORE(?x|?c)", pq._selectedVariables[1]);
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("10", pq._limit);
  ASSERT_EQ("15", pq._offset);
  ASSERT_EQ(size_t(2), pq._orderBy.size());
  ASSERT_EQ("?y", pq._orderBy[0]._key);
  ASSERT_FALSE(pq._orderBy[0]._desc);
  ASSERT_EQ("SCORE(?x|?c)", pq._orderBy[1]._key);
  ASSERT_TRUE(pq._orderBy[1]._desc);
  ASSERT_TRUE(pq._distinct);
  ASSERT_FALSE(pq._reduced);

  pq = SparqlParser::parse(
      "SELECT REDUCED ?x ?y WHERE \t {?x :myrel ?y}\n"
          "ORDER BY DESC(?x) ASC(?y) LIMIT 10 OFFSET 15");
  pq.expandPrefixes();
  ASSERT_EQ(0, pq._prefixes.size());
  ASSERT_EQ(2, pq._selectedVariables.size());
  ASSERT_EQ(1, pq._whereClauseTriples.size());
  ASSERT_EQ("10", pq._limit);
  ASSERT_EQ("15", pq._offset);
  ASSERT_EQ(size_t(2), pq._orderBy.size());
  ASSERT_EQ("?x", pq._orderBy[0]._key);
  ASSERT_TRUE(pq._orderBy[0]._desc);
  ASSERT_EQ("?y", pq._orderBy[1]._key);
  ASSERT_FALSE(pq._orderBy[1]._desc);
  ASSERT_FALSE(pq._distinct);
  ASSERT_TRUE(pq._reduced);

  pq = SparqlParser::parse(
      "SELECT ?x ?y WHERE {?x is-a Actor} LIMIT 10");
  pq.expandPrefixes();
  ASSERT_EQ("10", pq._limit);
}