/* * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Benoit Delbosc */ package org.nuxeo.elasticsearch.test.nxql; import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.IterableQueryResult; import org.nuxeo.ecm.core.api.PathRef; import org.nuxeo.ecm.core.api.VersioningOption; import org.nuxeo.ecm.core.query.sql.NXQL; import org.nuxeo.ecm.core.test.CoreFeature; import org.nuxeo.ecm.core.test.annotations.Granularity; import org.nuxeo.ecm.core.test.annotations.RepositoryConfig; import org.nuxeo.ecm.core.work.api.WorkManager; import org.nuxeo.elasticsearch.api.ElasticSearchAdmin; import org.nuxeo.elasticsearch.api.ElasticSearchIndexing; import org.nuxeo.elasticsearch.api.ElasticSearchService; import org.nuxeo.elasticsearch.api.EsResult; import org.nuxeo.elasticsearch.core.EsResultSetImpl; import org.nuxeo.elasticsearch.query.NxQueryBuilder; import org.nuxeo.elasticsearch.test.RepositoryElasticSearchFeature; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.LocalDeploy; import org.nuxeo.runtime.transaction.TransactionHelper; @RunWith(FeaturesRunner.class) @Features({ RepositoryElasticSearchFeature.class }) @LocalDeploy("org.nuxeo.elasticsearch.core:elasticsearch-test-contrib.xml") @RepositoryConfig(cleanup = Granularity.METHOD) public class TestCompareQueryAndFetch { @Inject protected CoreFeature coreFeature; @Inject protected CoreSession session; @Inject protected ElasticSearchService ess; @Inject protected ElasticSearchAdmin esa; @Inject protected ElasticSearchIndexing esi; private String proxyPath; @Before public void initWorkingDocuments() throws Exception { if (!TransactionHelper.isTransactionActive()) { TransactionHelper.startTransaction(); } Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); cal.set(2000, 1 - 1, 2, 3, 4, 5); cal.set(Calendar.MILLISECOND, 6); for (int i = 0; i < 5; i++) { String name = "file" + i; DocumentModel doc = session.createDocumentModel("/", name, "File"); doc.setPropertyValue("dc:title", "File" + i); doc.setPropertyValue("dc:nature", "Nature" + i); doc.setPropertyValue("dc:rights", "Rights" + i % 2); doc.setPropertyValue("dc:issued", cal); doc = session.createDocument(doc); } for (int i = 5; i < 10; i++) { String name = "note" + i; DocumentModel doc = session.createDocumentModel("/", name, "Note"); doc.setPropertyValue("dc:title", "Note" + i); doc.setPropertyValue("note:note", "Content" + i); doc.setPropertyValue("dc:nature", "Nature" + i); doc.setPropertyValue("dc:rights", "Rights" + i % 2); doc = session.createDocument(doc); } DocumentModel doc = session.createDocumentModel("/", "hidden", "HiddenFolder"); doc.setPropertyValue("dc:title", "HiddenFolder"); doc = session.createDocument(doc); DocumentModel folder = session.createDocumentModel("/", "folder", "Folder"); folder.setPropertyValue("dc:title", "Folder"); folder = session.createDocument(folder); DocumentModel file = session.getDocument(new PathRef("/file3")); DocumentModel proxy = session.publishDocument(file, folder); proxyPath = proxy.getPathAsString(); session.followTransition(new PathRef("/file1"), "delete"); session.followTransition(new PathRef("/note5"), "delete"); session.checkIn(new PathRef("/file2"), VersioningOption.MINOR, "for testing"); TransactionHelper.commitOrRollbackTransaction(); // wait for async jobs WorkManager wm = Framework.getLocalService(WorkManager.class); Assert.assertTrue(wm.awaitCompletion(20, TimeUnit.SECONDS)); Assert.assertEquals(0, esa.getPendingWorkerCount()); esa.refresh(); TransactionHelper.startTransaction(); } @After public void cleanWorkingDocuments() throws Exception { // prevent NXP-14686 bug that prevent cleanupSession to remove version session.removeDocument(new PathRef(proxyPath)); } protected String getDigest(IterableQueryResult docs) throws Exception { StringBuilder sb = new StringBuilder(); for (Map<String, Serializable> doc : docs) { List<String> keys = new ArrayList<>(doc.keySet()); Collections.sort(keys); Map<String, Serializable> sortedMap = new LinkedHashMap<>(); for (String key : keys) { Serializable value = doc.get(key); if (value instanceof Calendar) { // ISO 8601 value = String.format("%tFT%<tT.%<tL%<tz", (Calendar) value); } if (coreFeature.getStorageConfiguration().isDBS()) { if (key.equals("ecm:name") || key.equals("ecm:parentId")) { // MongoDB has extra keys in the result set, ignore them continue; } if (value == null) { // MongoDB returns explicit nulls continue; } } sortedMap.put(key, value); } sb.append(sortedMap.entrySet().toString()); sb.append("\n"); } return sb.toString(); } protected void assertSameDocumentLists(IterableQueryResult expected, IterableQueryResult actual) throws Exception { Assert.assertEquals(getDigest(expected), getDigest(actual)); } protected void compareESAndCore(String nxql) throws Exception { IterableQueryResult coreResult = session.queryAndFetch(nxql, NXQL.NXQL); EsResult esRes = ess.queryAndAggregate(new NxQueryBuilder(session).nxql(nxql).limit(20)); IterableQueryResult esResult = esRes.getRows(); assertSameDocumentLists(coreResult, esResult); coreResult.close(); esResult.close(); } @Test public void testSimpleSearchWithSort() throws Exception { compareESAndCore("select ecm:uuid, dc:title, dc:nature from Document order by ecm:uuid"); compareESAndCore("select ecm:uuid, dc:title from Document where ecm:currentLifeCycleState != 'deleted' order by ecm:uuid"); compareESAndCore("select ecm:uuid, dc:nature from File order by dc:nature, ecm:uuid"); // TODO some timezone issues here... // compareESAndCore("select ecm:uuid, dc:issued from File order by ecm:uuid"); } @Test public void testIteratorWithLimit() throws Exception { int LIMIT = 5; EsResult esRes = ess.queryAndAggregate(new NxQueryBuilder(session).nxql("select ecm:uuid From Document").limit(LIMIT)); try(IterableQueryResult res = esRes.getRows()) { // the number of doc in the iterator Assert.assertEquals(LIMIT, res.size()); // the total number of docs that match for the query Assert.assertEquals(20, ((EsResultSetImpl) res).totalSize()); } } }