/******************************************************************************* * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019) * * contact.vitam@culture.gouv.fr * * This software is a computer program whose purpose is to implement a digital archiving back-office system managing * high volumetry securely and efficiently. * * This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free * software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as * circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, * users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the * successive licensors have only limited liability. * * In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or * developing or reproducing the software by the user in light of its specific status of free software, that may mean * that it is complicated to manipulate, and that also therefore means that it is reserved for developers and * experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the * software's suitability as regards their requirements in conditions enabling the security of their systems and/or data * to be ensured and, more generally, to use and operate it in the same conditions as regards security. * * The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you * accept its terms. *******************************************************************************/ package fr.gouv.vitam.common.database.parser.request.multiple; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.and; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.eq; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.exists; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.flt; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.gt; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.gte; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.in; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.isNull; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.lte; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.match; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.matchPhrase; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.matchPhrasePrefix; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.missing; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.ne; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.nin; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.not; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.or; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.path; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.range; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.regex; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.search; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.size; import static fr.gouv.vitam.common.database.builder.query.QueryHelper.term; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.junit.BeforeClass; import org.junit.Test; import com.fasterxml.jackson.databind.JsonNode; import fr.gouv.vitam.common.database.builder.query.Query; import fr.gouv.vitam.common.database.builder.request.configuration.BuilderToken.FILTERARGS; import fr.gouv.vitam.common.database.builder.request.configuration.BuilderToken.PROJECTION; import fr.gouv.vitam.common.database.builder.request.configuration.BuilderToken.SELECTFILTER; import fr.gouv.vitam.common.database.builder.request.configuration.GlobalDatas; import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException; import fr.gouv.vitam.common.database.builder.request.multiple.Select; import fr.gouv.vitam.common.database.parser.request.GlobalDatasParser; import fr.gouv.vitam.common.database.parser.request.adapter.VarNameAdapter; import fr.gouv.vitam.common.exception.InvalidParseOperationException; import fr.gouv.vitam.common.json.JsonHandler; import fr.gouv.vitam.common.logging.VitamLogLevel; import fr.gouv.vitam.common.logging.VitamLoggerFactory; @SuppressWarnings("javadoc") public class SelectParserMultipleTest { private static JsonNode exampleBothEsMd; private static JsonNode exampleMd; @BeforeClass public static void init() throws InvalidParseOperationException { VitamLoggerFactory.setLogLevel(VitamLogLevel.INFO); exampleBothEsMd = JsonHandler.getFromString("{\"$roots\":[\"id0\"],\"$query\":[{\"$path\":[\"id1\",\"id2\"]}," + "{\"$and\":[{\"$exists\":\"mavar1\"},{\"$missing\":\"mavar2\"},{\"$isNull\":\"mavar3\"}," + "{\"$or\":[{\"$in\":{\"mavar4\":[1,2,\"maval1\"]}},{\"$nin\":{\"mavar5\":[\"maval2\",true]}}]}]}," + "{\"$not\":[{\"$size\":{\"mavar5\":5}},{\"$gt\":{\"mavar6\":7}},{\"$lte\":{\"mavar7\":8}}],\"$exactdepth\":4}," + "{\"$not\":[{\"$eq\":{\"mavar8\":5}},{\"$ne\":{\"mavar9\":\"ab\"}}," + "{\"$range\":{\"mavar10\":{\"$gte\":12,\"$lte\":20}}}],\"$depth\":10}," + "{\"$match_phrase\":{\"mavar11\":\"ceci est une phrase\"},\"$depth\":0}," + "{\"$match_phrase_prefix\":{\"mavar11\":\"ceci est une phrase\",\"$max_expansions\":10},\"$depth\":0}," + "{\"$flt\":{\"$fields\":[\"mavar12\",\"mavar13\"],\"$like\":\"ceci est une phrase\"},\"$depth\":1}," + "{\"$and\":[{\"$search\":{\"mavar13\":\"ceci est une phrase\"}},{\"$regex\":{\"mavar14\":\"^start?aa.*\"}}]}," + "{\"$and\":[{\"$term\":{\"mavar14\":\"motMajuscule\",\"mavar15\":\"simplemot\"}}]}," + "{\"$and\":[{\"$term\":{\"mavar16\":\"motMajuscule\",\"mavar17\":\"simplemot\"}}," + "{\"$or\":[{\"$eq\":{\"mavar19\":\"abcd\"}},{\"$match\":{\"mavar18\":\"quelques mots\"}}]}]}," + "{\"$regex\":{\"mavar14\":\"^start?aa.*\"}}],\"$filter\":{\"$offset\":100,\"$limit\":1000," + "\"$hint\":[\"cache\"],\"$orderby\":{\"maclef1\":1,\"maclef2\":-1,\"maclef3\":1}}," + "\"$projection\":{\"$fields\":{\"#dua\":1,\"#all\":1},\"$usage\":\"abcdef1234\"}}"); exampleMd = JsonHandler.getFromString("{ $roots : [ 'id0' ], $query : [ " + "{ $path : [ 'id1', 'id2'] }," + "{ $and : [ " + "{$exists : 'mavar1'}, " + "{$missing : 'mavar2'}, " + "{$isNull : 'mavar3'}, " + "{ $or : [ " + "{$in : { 'mavar4' : [1, 2, 'maval1'] }}, " + "{ $nin : { 'mavar5' : ['maval2', true] } } ] } ] }," + "{ $not : [ " + "{ $size : { 'mavar5' : 5 } }, " + "{ $gt : { 'mavar6' : 7 } }, " + "{ $lte : { 'mavar7' : 8 } } ] , $exactdepth : 4}," + "{ $not : [ " + "{ $eq : { 'mavar8' : 5 } }, " + "{ $ne : { 'mavar9' : 'ab' } }, " + "{ $range : { 'mavar10' : { $gte : 12, $lte : 20} } } ], $depth : 1}, " + "{ $and : [ { $term : { 'mavar14' : 'motMajuscule', 'mavar15' : 'simplemot' } } ] }, " + "{ $regex : { 'mavar14' : '^start?aa.*' } } " + "], " + "$filter : {$offset : 100, $limit : 1000, $hint : ['cache'], " + "$orderby : { maclef1 : 1 , maclef2 : -1, maclef3 : 1 } }," + "$projection : {$fields : {#dua : 1, #all : 1}, $usage : 'abcdef1234' } }"); } private static String createLongString(int size) { final StringBuilder sb = new StringBuilder(size); sb.append("{a:"); for (int i = 0; i < size; i++) { sb.append('a'); } sb.append("}"); return sb.toString(); } @Test public void testSanityCheckRequest() { final int oldValue = GlobalDatasParser.limitRequest; try { GlobalDatasParser.limitRequest = 1000; final String longfalsecode = createLongString(GlobalDatasParser.limitRequest + 100); final SelectParserMultiple request1 = new SelectParserMultiple(); request1.parse(JsonHandler.getFromString(longfalsecode)); fail("Should fail"); } catch (final InvalidParseOperationException e) { // nothing } finally { GlobalDatasParser.limitRequest = oldValue; } } @Test public void testParse() { try { final SelectParserMultiple request1 = new SelectParserMultiple(); request1.parse(exampleBothEsMd.deepCopy()); assertTrue("Should refuse the request since ES is not allowed", request1.hasFullTextQuery()); request1.parse(exampleMd.deepCopy()); assertFalse("Should accept the request since ES is not allowed", request1.hasFullTextQuery()); } catch (final Exception e) {} try { final SelectParserMultiple request1 = new SelectParserMultiple(); request1.parse(exampleBothEsMd.deepCopy()); assertNotNull(request1); assertTrue("Should refuse the request since ES is not allowed", request1.hasFullTextQuery()); final Select select = new Select(); select.addRoots("id0"); select.addQueries(path("id1", "id2")); select.addQueries( and().add(exists("mavar1"), missing("mavar2"), isNull("mavar3"), or().add(in("mavar4", 1, 2).add("maval1"), nin("mavar5", "maval2").add(true)))); select.addQueries( not().add(size("mavar5", 5), gt("mavar6", 7), lte("mavar7", 8)) .setExactDepthLimit(4)); select.addQueries(not() .add(eq("mavar8", 5), ne("mavar9", "ab"), range("mavar10", 12, true, 20, true)) .setDepthLimit(10)); select.addQueries(matchPhrase("mavar11", "ceci est une phrase") .setRelativeDepthLimit(0)); select.addQueries(matchPhrasePrefix("mavar11", "ceci est une phrase") .setMatchMaxExpansions(10) .setDepthLimit(0)); select.addQueries(flt("ceci est une phrase", "mavar12", "mavar13") .setRelativeDepthLimit(1)); select.addQueries(and().add(search("mavar13", "ceci est une phrase"), regex("mavar14", "^start?aa.*"))); select.addQueries(and() .add(term("mavar14", "motMajuscule").add("mavar15", "simplemot"))); select.addQueries( and().add(term("mavar16", "motMajuscule").add("mavar17", "simplemot"), or().add(eq("mavar19", "abcd"), match("mavar18", "quelques mots")))); select.addQueries(regex("mavar14", "^start?aa.*")); select.setLimitFilter(100, 1000).addHintFilter(FILTERARGS.CACHE.exactToken()); select.addOrderByAscFilter("maclef1") .addOrderByDescFilter("maclef2").addOrderByAscFilter("maclef3"); select.addUsedProjection("#dua", "#all").setUsageProjection("abcdef1234"); final SelectParserMultiple request2 = new SelectParserMultiple(); request2.parse(select.getFinalSelect()); assertNotNull(request2); final List<Query> query1 = request1.getRequest().getQueries(); final List<Query> query2 = request2.getRequest().getQueries(); for (int i = 0; i < query1.size(); i++) { if (!query1.get(i).toString().equals(query2.get(i).toString())) { System.err.println(query1.get(i)); System.err.println(query2.get(i)); } assertTrue("TypeRequest should be equal", query1.get(i).toString().equals(query2.get(i).toString())); } assertTrue("Projection should be equal", request1.getRequest().getProjection().toString() .equals(request2.getRequest().getProjection().toString())); assertTrue("OrderBy should be equal", request1.getRequest().getFilter().toString() .equals(request2.getRequest().getFilter().toString())); assertEquals(request1.getLastDepth(), request2.getLastDepth()); assertEquals(request1.hasFullTextQuery(), request2.hasFullTextQuery()); assertEquals(request1.getRequest().getRoots().toString(), request2.getRequest().getRoots().toString()); assertEquals(request1.getRequest().getFinalSelect().toString(), request2.getRequest().getFinalSelect().toString()); assertTrue("Command should be equal", request1.toString().equals(request2.toString())); } catch (final Exception e) { e.printStackTrace(); fail(e.getMessage()); } } @Test public void testFilterParse() { final SelectParserMultiple request = new SelectParserMultiple(); final Select select = new Select(); try { // empty request.filterParse(select.getFilter()); assertNull("Hint should be null", request.getRequest().getFilter().get(SELECTFILTER.HINT.exactToken())); assertNotNull("Limit should not be null", request.getRequest().getFilter() .get(SELECTFILTER.LIMIT.exactToken())); assertNull("Offset should be null", request.getRequest().getFilter() .get(SELECTFILTER.OFFSET.exactToken())); assertNull("OrderBy should be null", request.getRequest().getFilter() .get(SELECTFILTER.ORDERBY.exactToken())); // hint set select.addHintFilter(FILTERARGS.CACHE.exactToken()); request.filterParse(select.getFilter()); assertEquals("Hint should be True", FILTERARGS.CACHE.exactToken(), request.getRequest().getFilter().get(SELECTFILTER.HINT.exactToken()) .get(0).asText()); // hint reset select.resetHintFilter(); request.filterParse(select.getFilter()); assertNull("Hint should be null", request.getRequest().getFilter().get(SELECTFILTER.HINT.exactToken())); // limit set select.setLimitFilter(0, 1000); request.filterParse(select.getFilter()); assertEquals(1000, request.getRequest().getFilter().get(SELECTFILTER.LIMIT.exactToken()) .asLong()); assertNull("Offset should be null", request.getRequest().getFilter() .get(SELECTFILTER.OFFSET.exactToken())); // offset set select.setLimitFilter(100, 0); request.filterParse(select.getFilter()); assertEquals(100, request.getRequest().getFilter().get(SELECTFILTER.OFFSET.exactToken()) .asLong()); assertEquals(GlobalDatas.LIMIT_LOAD, request.getRequest().getFilter().get(SELECTFILTER.LIMIT.exactToken()) .asLong()); // orderBy set through array select.addOrderByAscFilter("var1", "var2").addOrderByDescFilter("var3"); request.filterParse(select.getFilter()); assertNotNull("OrderBy should not be null", request.getRequest().getFilter() .get(SELECTFILTER.ORDERBY.exactToken())); // check both assertEquals(3, request.getRequest().getFilter() .get(SELECTFILTER.ORDERBY.exactToken()).size()); for (final Iterator<Entry<String, JsonNode>> iterator = request.getRequest().getFilter() .get(SELECTFILTER.ORDERBY.exactToken()).fields(); iterator .hasNext();) { final Entry<String, JsonNode> entry = iterator.next(); if (entry.getKey().equals("var1")) { assertEquals(1, entry.getValue().asInt()); } if (entry.getKey().equals("var2")) { assertEquals(1, entry.getValue().asInt()); } if (entry.getKey().equals("var3")) { assertEquals(-1, entry.getValue().asInt()); } } // orderBy set through composite select.resetOrderByFilter(); request.filterParse(select.getFilter()); assertNull("OrderBy should be null", request.getRequest().getFilter() .get(SELECTFILTER.ORDERBY.exactToken())); } catch (final InvalidParseOperationException e) { e.printStackTrace(); fail(e.getMessage()); } } @Test public void testProjectionParse() { final SelectParserMultiple request = new SelectParserMultiple(); final Select select = new Select(); try { // empty rootNode request.projectionParse(select.getProjection()); assertEquals("Projection should be empty", 0, request.getRequest().getProjection().size()); // contractId set select.setUsageProjection("abcd"); request.projectionParse(select.getProjection()); assertNotNull("Projection Usage should not be empty", request.getRequest() .getProjection().get(PROJECTION.USAGE.exactToken())); // projection set but empty select.addUsedProjection((String) null); // empty set request.projectionParse(select.getProjection()); assertNotNull("Projection Usage should not be be empty", request.getRequest() .getProjection().get(PROJECTION.USAGE.exactToken())); assertEquals("Projection should not be empty", 1, request.getRequest().getProjection().size()); // projection set select.addUsedProjection("var"); // empty set request.projectionParse(select.getProjection()); assertNotNull("Projection Usage should not be be empty", request.getRequest() .getProjection().get(PROJECTION.USAGE.exactToken())); assertEquals("Projection should not be empty", 2, request.getRequest().getProjection().size()); // reset select.resetUsageProjection().resetUsedProjection(); request.projectionParse(select.getProjection()); assertNull("Projection Usage should be empty", request.getRequest() .getProjection().get(PROJECTION.USAGE.exactToken())); assertEquals("Projection should be empty", 0, request.getRequest().getProjection().size()); // not empty set select.addUsedProjection("var1").addUnusedProjection("var2"); request.projectionParse(select.getProjection()); assertEquals("Projection should not be empty", 1, request.getRequest().getProjection().size()); assertEquals(2, request.getRequest().getProjection() .get(PROJECTION.FIELDS.exactToken()).size()); for (final Iterator<Entry<String, JsonNode>> iterator = request.getRequest().getProjection() .get(PROJECTION.FIELDS.exactToken()).fields(); iterator .hasNext();) { final Entry<String, JsonNode> entry = iterator.next(); if (entry.getKey().equals("var1")) { assertEquals(1, entry.getValue().asInt()); } if (entry.getKey().equals("var2")) { assertEquals(0, entry.getValue().asInt()); } } } catch (final InvalidParseOperationException e) { e.printStackTrace(); fail(e.getMessage()); } } @Test public void testSelectParser() throws InvalidParseOperationException { final SelectParserMultiple request = new SelectParserMultiple(); request.parse(exampleMd.deepCopy()); assertNotNull(request); final SelectParserMultiple request2 = new SelectParserMultiple(new VarNameAdapter()); assertNotNull(request2); } @Test public void testParseQueryOnly() throws InvalidParseOperationException { final SelectParserMultiple request = new SelectParserMultiple(); final String ex = "{}"; request.parseQueryOnly(ex); assertNotNull(request); } @Test public void testAddConditionParseSelect() throws InvalidParseOperationException, InvalidCreateOperationException { final SelectParserMultiple request = new SelectParserMultiple(); final Select select = new Select(); select.addQueries(and().add(term("var01", "value1"), gte("var02", 3))); select.addQueries(and().add(term("var11", "value2"), gte("var12", 4))); select.addOrderByAscFilter("var1").addOrderByDescFilter("var2").addUsedProjection("var3") .addUnusedProjection("var4"); request.parse(select.getFinalSelect()); assertNotNull(request.getRequest()); request.addCondition(eq("var5", "value")); assertEquals( "{\"$roots\":[]," + "\"$query\":[{\"$and\":[{\"$and\":[{\"$term\":{\"var01\":\"value1\"}},{\"$gte\":{\"var02\":3}}]}," + "{\"$eq\":{\"var5\":\"value\"}}]}," + "{\"$and\":[{\"$term\":{\"var11\":\"value2\"}},{\"$gte\":{\"var12\":4}}]}]," + "\"$filter\":{\"$limit\":10000,\"$orderby\":{\"var1\":1,\"var2\":-1}}," + "\"$projection\":{\"$fields\":{\"var3\":1,\"var4\":0}}}", request.getRootNode().toString()); } @Test public void testInternalParseSelect() throws InvalidParseOperationException { final SelectParserMultiple request = new SelectParserMultiple(); final String s = "[ [ 'id0' ], { $path : [ 'id1', 'id2'] }, {$mult : false }, {} ]"; request.parse(JsonHandler.getFromString(s)); assertNotNull(request); } @Test public void testWrongParseSelect() throws InvalidParseOperationException, InvalidCreateOperationException { final SelectParserMultiple request = new SelectParserMultiple(); String s = "{\"$roots\":[]," + "\"$query\":[{\"$and\":[{\"$and\":[{\"$term\":{\"_id\":\"value1\"}},{\"$gte\":{\"var02\":3}}]}," + "{\"$eq\":{\"var5\":\"value\"}}]}," + "{\"$and\":[{\"$term\":{\"var11\":\"value2\"}},{\"$gte\":{\"var12\":4}}]}]," + "\"$filter\":{\"$limit\":10000,\"$orderby\":{\"var1\":1,\"var2\":-1}}," + "\"$projection\":{\"$fields\":{\"var3\":1,\"var4\":0}}}"; try { request.parse(JsonHandler.getFromString(s)); fail("Should fail"); } catch (final InvalidParseOperationException e) { // OK } s = "{\"$roots\":[]," + "\"$query\":[{\"$and\":[{\"$and\":[{\"$term\":{\"var01\":\"value1\"}},{\"$gte\":{\"var02\":3}}]}," + "{\"$eq\":{\"var5\":\"value\"}}]}," + "{\"$and\":[{\"$term\":{\"var11\":\"value2\"}},{\"$gte\":{\"var12\":4}}]}]," + "\"$filter\":{\"$limit\":10000,\"$orderby\":{\"var1\":1,\"var2\":-1}}," + "\"$projection\":{\"$fields\":{\"_id\":1,\"var4\":0}}}"; try { request.parse(JsonHandler.getFromString(s)); fail("Should fail"); } catch (final InvalidParseOperationException e) { // OK } s = "{\"$roots\":[]," + "\"$query\":[{\"$and\":[{\"$and\":[{\"$term\":{\"var01\":\"value1\"}},{\"$gte\":{\"var02\":3}}]}," + "{\"$eq\":{\"var5\":\"value\"}}]}," + "{\"$and\":[{\"$term\":{\"var11\":\"value2\"}},{\"$gte\":{\"var12\":4}}]}]," + "\"$filter\":{\"$limit\":10000,\"$orderby\":{\"_id\":1,\"var2\":-1}}," + "\"$projection\":{\"$fields\":{\"var3\":1,\"var4\":0}}}"; try { request.parse(JsonHandler.getFromString(s)); fail("Should fail"); } catch (final InvalidParseOperationException e) { // OK } } }