/* * 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.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; 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; /** * */ public class IntersectionUnionPagingIT { private static final Logger logger = LoggerFactory.getLogger( IntersectionUnionPagingIT.class ); private static final String unionScan = "select * where (field1Or > '00000000' OR field2Or > '00000000') AND fieldDate = '0000-00-00'"; private static final String scanUnion = "select * where fieldDate = '0000-00-00' AND (field1Or > '00000000' OR field2Or > '00000000') "; private static final int PAGE_SIZE = 300; @ClassRule public static CoreITSetup setup = new CoreITSetupImpl( ); @Rule public CoreApplication app = new CoreApplication( setup ); @Test public void testUnionPagingCollection() throws Exception { final CollectionIoHelper collectionIoHelper = new CollectionIoHelper( app ); Set<String> created = performSetup( collectionIoHelper ); testUnionPaging( collectionIoHelper, unionScan, created ); testUnionPaging( collectionIoHelper, scanUnion, created ); } @Test public void testUnionPagingConnection() throws Exception { final ConnectionHelper connectionHelper = new ConnectionHelper( app ); Set<String> created = performSetup( connectionHelper ); testUnionPaging( connectionHelper, unionScan, created ); testUnionPaging( connectionHelper, scanUnion, created ); } private Set<String> performSetup( final IoHelper io ) throws Exception { io.doSetup(); int size =10; long start = System.currentTimeMillis(); logger.info( "Writing {} entities.", size ); final String zeros = String.format( "%08d", 0 ); Set<String> names = new HashSet<String>( size ); for ( int i = 0; i < size; i++ ) { Map<String, Object> entity = new HashMap<String, Object>(); final String name = String.valueOf( i ); entity.put( "name", name ); entity.put( "fieldDate", "0000-00-00" ); String field1 = String.format( "%08d", i + 1 ); String field2; //use a value slightly smaller than page size, since we want to simulate //the cursor issues with union queries if ( i < size - 10 ) { field2 = zeros; } else { field2 = String.format( "%08d", i + 1 ); } names.add( name ); entity.put( "field1Or", field1 ); entity.put( "field2Or", field2 ); Entity saved = io.writeEntity( entity ); if (logger.isDebugEnabled()) { logger.debug("Writing entity with id '{}'", saved.getUuid()); } } app.waitForQueueDrainAndRefreshIndex(); long stop = System.currentTimeMillis(); logger.info( "Writes took {} ms", stop - start ); return Collections.unmodifiableSet( names ); } private void testUnionPaging( final IoHelper io, final String queryString, final Set<String> expectedResults ) throws Exception { Set<String> newSets = new HashSet<String>( expectedResults ); //our field1Or has a result size < our page size, so it shouldn't blow up when the // cursor is getting created the leaf iterator should insert it's own "no value left" i // not the cursor Query query = Query.fromQL( queryString ); query.setLimit( PAGE_SIZE ); Results results; long 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++ ) { final String name = results.getEntities().get( i ).getName(); assertTrue( "Value should not be returned twice", newSets.contains( name ) ); newSets.remove( name ); } query.setCursor( results.getCursor() ); } while ( results.getCursor() != null ); long stop = System.currentTimeMillis(); logger.info( "Query took {} ms to return {} entities", stop - start, expectedResults.size() ); assertEquals( "All names returned", 0, newSets.size() ); } }