/* * 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.usergrid.persistence.query; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.usergrid.CoreApplication; import org.apache.usergrid.CoreITSetup; import org.apache.usergrid.CoreITSetupImpl; import org.apache.usergrid.persistence.Entity; import org.apache.usergrid.persistence.Results; import org.apache.usergrid.persistence.Query; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @author tnine */ public class IteratingQueryIT { private static final Logger logger = LoggerFactory.getLogger( IteratingQueryIT.class ); @ClassRule public static CoreITSetup setup = new CoreITSetupImpl( ); @Rule public CoreApplication app = new CoreApplication( setup ); @Test public void allInCollection() throws Exception { allIn( new CollectionIoHelper( app ) ); } @Test public void allInConnection() throws Exception { allIn( new ConnectionHelper( app ) ); } @Test public void allInConnectionNoType() throws Exception { allIn( new ConnectionNoTypeHelper( app ) ); } @Test public void multiOrderByCollection() throws Exception { multiOrderBy( new CollectionIoHelper( app ) ); } @Test public void multiOrderByComplexUnionCollection() throws Exception { multiOrderByComplexUnion( new CollectionIoHelper( app ) ); } @Test public void multiOrderByComplexUnionConnection() throws Exception { multiOrderByComplexUnion( new CollectionIoHelper( app ) ); } @Test public void multOrderByConnection() throws Exception { multiOrderBy( new ConnectionHelper( app ) ); } @Test public void orderByWithNotCollection() throws Exception { notOrderBy( new CollectionIoHelper( app ) ); } @Test public void orderByWithNotConnection() throws Exception { notOrderBy( new ConnectionHelper( app ) ); } @Test public void singleOrderByBoundRangeScanAscCollection() throws Exception { singleOrderByBoundRangeScanAsc( new CollectionIoHelper( app ) ); } @Test public void singleOrderByBoundRangeScanAscConnection() throws Exception { singleOrderByBoundRangeScanAsc( new ConnectionHelper( app ) ); } @Test public void singleOrderByBoundRangeScanDescCollection() throws Exception { singleOrderByBoundRangeScanDesc( new CollectionIoHelper( app ) ); } @Test public void singleOrderByBoundRangeScanDescConnection() throws Exception { singleOrderByBoundRangeScanDesc( new ConnectionHelper( app ) ); } @Test public void singleOrderByComplexIntersectionCollection() throws Exception { singleOrderByComplexIntersection( new CollectionIoHelper( app ) ); } @Test public void singleOrderByComplexIntersectionConnection() throws Exception { singleOrderByComplexIntersection( new ConnectionHelper( app ) ); } @Test public void singleOrderByComplexUnionCollection() throws Exception { singleOrderByComplexUnion( new CollectionIoHelper( app ) ); } @Test public void singleOrderByComplexUnionConnection() throws Exception { singleOrderByComplexUnion( new ConnectionHelper( app ) ); } @Test public void singleOrderByIntersectionCollection() throws Exception { singleOrderByIntersection( new CollectionIoHelper( app ) ); } @Test public void singleOrderByIntersectionConnection() throws Exception { singleOrderByIntersection( new ConnectionHelper( app ) ); } @Test public void singleOrderByLessThanLimitCollection() throws Exception { singleOrderByLessThanLimit( new CollectionIoHelper( app ) ); } @Test public void singleOrderByLessThanLimitConnection() throws Exception { singleOrderByLessThanLimit( new ConnectionHelper( app ) ); } @Test public void singleOrderByMaxLimitCollection() throws Exception { singleOrderByMaxLimit( new CollectionIoHelper( app ) ); } @Test public void singleOrderByMaxLimitConnection() throws Exception { singleOrderByMaxLimit( new ConnectionHelper( app ) ); } @Test public void singleOrderByNoIntersectionCollection() throws Exception { singleOrderByNoIntersection( new CollectionIoHelper( app ) ); } @Test public void singleOrderByNoIntersectionConnection() throws Exception { singleOrderByNoIntersection( new CollectionIoHelper( app ) ); } @Test public void singleOrderByNotCollection() throws Exception { singleOrderByNot( new CollectionIoHelper( app ) ); } @Test public void singleOrderByNotConnection() throws Exception { singleOrderByNot( new ConnectionHelper( app ) ); } @Test public void singleOrderBySameRangeScanGreaterCollection() throws Exception { singleOrderBySameRangeScanGreater( new CollectionIoHelper( app ) ); } @Test public void singleOrderBySameRangeScanGreaterConnection() throws Exception { singleOrderBySameRangeScanGreater( new ConnectionHelper( app ) ); } @Test public void singleOrderBySameRangeScanGreaterThanEqualCollection() throws Exception { singleOrderBySameRangeScanGreaterThanEqual( new CollectionIoHelper( app ) ); } @Test public void singleOrderBySameRangeScanLessCollection() throws Exception { singleOrderBySameRangeScanLessEqual( new CollectionIoHelper( app ) ); } @Test public void singleOrderBySameRangeScanLessConnection() throws Exception { singleOrderBySameRangeScanLessEqual( new ConnectionHelper( app ) ); } @Test public void singleOrderBySameRangeScanLessThanEqualCollection() throws Exception { singleOrderBySameRangeScanLessThanEqual( new CollectionIoHelper( app ) ); } @Test public void singleOrderBySameRangeScanLessThanEqualConnection() throws Exception { singleOrderBySameRangeScanLessThanEqual( new ConnectionHelper( app ) ); } class ConnectionNoTypeHelper extends ConnectionHelper { public ConnectionNoTypeHelper( final CoreApplication app ) { super( app ); } /** * (non-Javadoc) @see org.apache.usergrid.persistence.query.SingleOrderByMaxLimitCollection * .ConnectionHelper#getResults * (org.apache.usergrid.persistence.Query) */ @Override public Results getResults( Query query ) throws Exception { query.setConnectionType( CONNECTION ); // don't set it on purpose query.setEntityType( null ); return app.getEntityManager().searchTargetEntities(rootEntity, query); } } public void singleOrderByMaxLimit( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); entity.put("name", String.valueOf(i)); io.writeEntity(entity); //we have to sleep, or we kill embedded cassandra } app.waitForQueueDrainAndRefreshIndex(); Thread.sleep(1000); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL("order by created" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( String.valueOf( count ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( size, count ); } protected void singleOrderByIntersection( IoHelper io ) throws Exception { io.doSetup(); int size = 70; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int intersectIncrement = 5; long start = System.currentTimeMillis(); List<String> expected = new ArrayList<String>( size / intersectIncrement ); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean intersect = i % intersectIncrement == 0; entity.put( "name", String.valueOf( i ) ); // if we hit the increment, set this to true entity.put( "intersect", intersect ); io.writeEntity( entity ); if ( intersect ) { expected.add( name ); } } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where intersect = true order by created asc" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0 ; i< results.size(); i++) { assertEquals( expected.get( count++ ), results.getEntities().get( i ).getName() ); } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( expected.size(), count ); } protected void singleOrderByComplexIntersection( IoHelper io ) throws Exception { int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int intersectIncrement = 5; int secondIncrement = 9; long start = System.currentTimeMillis(); io.doSetup(); logger.info( "Writing {} entities.", size ); List<String> expectedResults = new ArrayList<String>( size / secondIncrement ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean intersect1 = i % intersectIncrement == 0; boolean intersect2 = i % secondIncrement == 0; entity.put( "name", name ); // if we hit the increment, set this to true entity.put( "intersect", intersect1 ); entity.put( "intersect2", intersect2 ); io.writeEntity( entity ); if ( intersect1 && intersect2 ) { expectedResults.add( name ); } } this.app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where intersect = true AND intersect2 = true order by created" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expectedResults.get( count ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( expectedResults.size(), count ); } protected void singleOrderByNoIntersection( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int secondIncrement = 9; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", String.valueOf( i ) ); // if we hit the increment, set this to true entity.put( "intersect", false ); entity.put( "intersect2", i % secondIncrement == 0 ); io.writeEntity( entity ); } long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where intersect = true AND intersect2 = true order by created" ); query.setLimit( queryLimit ); start = System.currentTimeMillis(); Results results = io.getResults( query ); // now do simple ordering, should be returned in order stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, 0 ); assertEquals( 0, results.size() ); } protected void singleOrderByComplexUnion( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int intersectIncrement = 5; int secondIncrement = 9; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expectedResults = new ArrayList<String>( size / secondIncrement ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean intersect1 = i % intersectIncrement == 0; boolean intersect2 = i % secondIncrement == 0; entity.put( "name", name ); // if we hit the increment, set this to true entity.put( "intersect", intersect1 ); entity.put( "intersect2", intersect2 ); io.writeEntity( entity ); if ( intersect1 || intersect2 ) { expectedResults.add( name ); } } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where intersect = true OR intersect2 = true order by created" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expectedResults.get( count ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( expectedResults.size(), count ); } protected void singleOrderByNot( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int intersectIncrement = 5; int secondIncrement = 9; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expectedResults = new ArrayList<String>( size / secondIncrement ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean intersect1 = i % intersectIncrement == 0; boolean intersect2 = i % secondIncrement == 0; entity.put( "name", name ); // if we hit the increment, set this to true entity.put( "intersect", intersect1 ); entity.put( "intersect2", intersect2 ); io.writeEntity( entity ); if ( !( intersect1 && intersect2 ) ) { expectedResults.add( name ); } } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where NOT (intersect = true AND intersect2 = true) order by created" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expectedResults.get( count ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( expectedResults.size(), count ); } protected void singleOrderByLessThanLimit( IoHelper io ) throws Exception { io.doSetup(); int size = 10; int queryLimit = Query.MAX_LIMIT; int matchMax = queryLimit - 1; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( matchMax ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); boolean searched = i < matchMax; Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "searched", searched ); io.writeEntity( entity ); if ( searched ) { expected.add( name ); } } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where searched = true order by created" ); query.setLimit( queryLimit ); int count = 0; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( count ), results.getEntities().get( i ).getName() ); count++; } assertTrue( results.getCursor() == null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( expected.size(), count ); } protected void singleOrderBySameRangeScanLessThanEqual( IoHelper io ) throws Exception { io.doSetup(); int size = 10; int queryLimit = 5; int startValue = 5; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } this.app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where index >= "+ startValue + " order by index desc" ); query.setLimit( queryLimit ); int count = 0; int delta = size - startValue; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( size - count -1 ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - delta, count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } protected void singleOrderBySameRangeScanLessEqual( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = 5; int startValue = 10; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } this.app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where index >= "+ startValue + " order by index desc" ); query.setLimit( queryLimit ); int count = 0; int delta = size - startValue; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( size - count - 1 ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - delta, count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } protected void singleOrderBySameRangeScanGreaterThanEqual( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = 10; int startValue = 10; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where index >= "+ startValue + " order by index desc" ); query.setLimit( queryLimit ); int count = 0; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( size - count - 1 ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - startValue, count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } protected void singleOrderBySameRangeScanGreater( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = 10; int startValue = 9; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } app.waitForQueueDrainAndRefreshIndex(); Thread.sleep(500); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( "select * where index >= "+ startValue + " order by index desc" ); query.setLimit( queryLimit ); int count = 0; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( size - count - 1 ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - startValue , count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } protected void singleOrderByBoundRangeScanDesc( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = 10; int startValue = 5; int endValue = 15; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( String.format( "select * where index >= %d AND index <= %d order by index desc", startValue, endValue ) ); query.setLimit( queryLimit ); int count = 0; int delta = size - endValue; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( size - count - delta ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - startValue - delta +1 , count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } protected void singleOrderByBoundRangeScanAsc( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = 10; int startValue = 5; int endValue = 15; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); List<String> expected = new ArrayList<String>( size ); for ( int i = 0; i < size; i++ ) { String name = String.valueOf( i ); Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", name ); entity.put( "index", i ); io.writeEntity( entity ); expected.add( name ); } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL( String.format( "select * where index >= %d AND index <= %d order by index asc", startValue, endValue ) ); query.setLimit( queryLimit ); int count = 0; int delta = size - endValue; start = System.currentTimeMillis(); // now do simple ordering, should be returned in order Results results; do { results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( expected.get( delta + count ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.hasCursor() ); assertEquals( expected.size() - startValue - delta + 1, count ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); } /** * Tests that when an empty query is issued, we page through all entities correctly * * @param io the io helper */ protected void allIn( IoHelper io ) throws Exception { io.doSetup(); int size = 30; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", String.valueOf( i ) ); io.writeEntity( entity ); } this.app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info("Writes took {} ms", stop - start ); Query query = new Query(); query.setLimit( 10 ); int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( String.valueOf( size - count -1 ), results.getEntities().get( i ).getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( size, count ); } protected void multiOrderBy( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection Set<Entity> sortedResults = new TreeSet<Entity>( new Comparator<Entity>() { @Override public int compare( Entity o1, Entity o2 ) { boolean o1Boolean = ( Boolean ) o1.getProperty( "boolean" ); boolean o2Boolean = ( Boolean ) o2.getProperty( "boolean" ); if ( o1Boolean != o2Boolean ) { if ( o1Boolean ) { return -1; } return 1; } int o1Index = ( Integer ) o1.getProperty( "index" ); int o2Index = ( Integer ) o2.getProperty( "index" ); if ( o1Index > o2Index ) { return 1; } else if ( o2Index > o1Index ) { return -1; } return 0; } } ); long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean bool = i % 2 == 0; entity.put( "name", name ); entity.put( "boolean", bool ); /** * we want them to be ordered from the "newest" time uuid to the oldec since we * have a low cardinality value as the first second clause. This way the test *won't accidentally pass b/c the UUID ordering matches the index ordering. If we were *to reverse the value of index (size-i) the test would pass incorrectly */ entity.put( "index", i ); Entity saved = io.writeEntity( entity ); sortedResults.add( saved ); } long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); app.waitForQueueDrainAndRefreshIndex(); Query query = Query.fromQL( "select * order by boolean desc, index asc" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); Iterator<Entity> itr = sortedResults.iterator(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { Entity expected = itr.next(); Entity returned = results.getEntities().get( i ); assertEquals( "Order incorrect", expected.getName(), returned.getName() ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( sortedResults.size(), count ); } protected void multiOrderByComplexUnion( IoHelper io ) throws Exception { io.doSetup(); int size = 20; int queryLimit = Query.MAX_LIMIT; // the number of entities that should be written including an intersection int intersectIncrement = 5; int secondIncrement = 9; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); Set<Entity> sortedResults = new TreeSet<Entity>( new Comparator<Entity>() { @Override public int compare( Entity o1, Entity o2 ) { long o1Index = ( Long ) o1.getProperty( "created" ); long o2Index = ( Long ) o2.getProperty( "created" ); if ( o1Index > o2Index ) { return 1; } else if ( o2Index > o1Index ) { return -1; } boolean o1Boolean = ( Boolean ) o1.getProperty( "intersect" ); boolean o2Boolean = ( Boolean ) o2.getProperty( "intersect" ); if ( o1Boolean != o2Boolean ) { if ( o1Boolean ) { return -1; } return 1; } return 0; } } ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); String name = String.valueOf( i ); boolean intersect1 = i % intersectIncrement == 0; boolean intersect2 = i % secondIncrement == 0; entity.put( "name", name ); // if we hit the increment, set this to true entity.put( "intersect", intersect1 ); entity.put( "intersect2", intersect2 ); Entity e = io.writeEntity( entity ); if ( intersect1 || intersect2 ) { sortedResults.add( e ); } } long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); app.waitForQueueDrainAndRefreshIndex(); Query query = Query.fromQL( "select * where intersect = true OR intersect2 = true order by created, intersect desc" ); query.setLimit( queryLimit ); int count = 0; Results results; start = System.currentTimeMillis(); Iterator<Entity> expected = sortedResults.iterator(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( Entity result : results.getEntities() ) { assertEquals( expected.next(), result ); count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( sortedResults.size(), count ); } /** * Tests that when an empty query is issued, we page through all entities correctly * * @param io the io helper */ protected void notOrderBy( IoHelper io ) throws Exception { io.doSetup(); /** * Leave this as a large size. We have to write over 1k to reproduce this issue */ int size = 20; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); entity.put( "name", String.valueOf( i ) ); entity.put( "boolean", (i % 2 == 0)); entity.put( "index", i); io.writeEntity( entity ); } this.app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); Query query = Query.fromQL("select * where NOT boolean = false order by index asc"); query.setLimit( 2 ); int index = 0; int count = 0; Results results; start = System.currentTimeMillis(); do { // now do simple ordering, should be returned in order results = io.getResults( query ); for ( int i = 0; i < results.size(); i++ ) { assertEquals( String.valueOf( index ), results.getEntities().get( i ).getName() ); index +=2; count++; } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, count ); assertEquals( size/2, count ); } }