Skip to content

Commit ef351e4

Browse files
authored
GEOMESA-3580 FSDS - Use optimized sorting (#3547)
1 parent 0219d34 commit ef351e4

3 files changed

Lines changed: 40 additions & 12 deletions

File tree

geomesa-fs/geomesa-fs-datastore/src/main/scala/org/locationtech/geomesa/fs/data/FileSystemFeatureStore.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class FileSystemFeatureStore(
8181
val filter = Option(query.getFilter).filter(_ != Filter.INCLUDE)
8282
new ManagedScan(new FileSystemScanner(storage, query, readThreads), Timeout(timeout), query.getTypeName, filter)
8383
}
84+
8485
// get a closeable java iterator that DelegateSimpleFeatureIterator will process correctly
8586
val iter = new FileSystemFeatureIterator(reader)
8687

@@ -91,11 +92,11 @@ class FileSystemFeatureStore(
9192
new DelegateSimpleFeatureReader(transformSft, new DelegateSimpleFeatureIterator(iter))
9293
}
9394

94-
override def canLimit(query: Query): Boolean = false
9595
override def canTransact: Boolean = false
9696
override def canEvent: Boolean = false
9797
override def canReproject: Boolean = false
98-
override def canSort(query: Query): Boolean = false
98+
override def canLimit(query: Query): Boolean = true
99+
override def canSort(query: Query): Boolean = true
99100
override def canFilter(query: Query): Boolean = true
100101
override def canRetype(query: Query): Boolean = true
101102

geomesa-fs/geomesa-fs-datastore/src/test/scala/org/locationtech/geomesa/fs/data/FileSystemDataStoreTest.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import org.apache.commons.io.FileUtils
1313
import org.geotools.api.data.{DataStoreFinder, Query, Transaction}
1414
import org.geotools.api.feature.simple.{SimpleFeature, SimpleFeatureType}
1515
import org.geotools.api.filter.Filter
16+
import org.geotools.api.filter.sort.SortOrder
1617
import org.geotools.filter.text.ecql.ECQL
1718
import org.geotools.geometry.jts.ReferencedEnvelope
1819
import org.locationtech.geomesa.features.ScalaSimpleFeature
20+
import org.locationtech.geomesa.filter.FilterHelper
1921
import org.locationtech.geomesa.fs.storage.core.StorageKeys
2022
import org.locationtech.geomesa.utils.collection.CloseableIterator
2123
import org.locationtech.geomesa.utils.geotools.{CRS_EPSG_4326, FeatureUtils, SimpleFeatureTypes}
@@ -291,6 +293,25 @@ class FileSystemDataStoreTest extends SpecificationWithJUnit with BeforeAfterAll
291293
}
292294
}
293295

296+
"support sorting and limiting" in {
297+
foreach(dsParams) { params =>
298+
WithClose(DataStoreFinder.getDataStore(params.asJava)) { ds =>
299+
foreach(Seq(SortOrder.ASCENDING, SortOrder.DESCENDING)) { sortOrder =>
300+
val query = new Query(sft.getTypeName)
301+
query.setSortBy(FilterHelper.ff.sort("name", sortOrder))
302+
query.setMaxFeatures(1)
303+
val results = CloseableIterator(ds.getFeatureReader(query, Transaction.AUTO_COMMIT)).toList
304+
results must haveSize(1)
305+
if (sortOrder == SortOrder.ASCENDING) {
306+
results.head.getID mustEqual "0"
307+
} else {
308+
results.head.getID mustEqual "9"
309+
}
310+
}
311+
}
312+
}
313+
}
314+
294315
"support append without fid" in {
295316
foreach(dsParams) { params =>
296317
val dir = Files.createTempDirectory("fsds-test-append-without-fid").toFile

geomesa-fs/geomesa-fs-storage/geomesa-fs-storage-core/src/main/scala/org/locationtech/geomesa/fs/storage/core/FileSystemStorage.scala

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.locationtech.geomesa.fs.storage.core.utils.FileSize.UpdatingFileSizeE
2626
import org.locationtech.geomesa.fs.storage.core.utils.{FileSize, FileSystemThreadedReader}
2727
import org.locationtech.geomesa.index.index.attribute.AttributeIndexKey
2828
import org.locationtech.geomesa.index.planning.QueryRunner
29+
import org.locationtech.geomesa.index.utils.SortingSimpleFeatureIterator
2930
import org.locationtech.geomesa.utils.collection.CloseableIterator
3031
import org.locationtech.geomesa.utils.io.{CloseQuietly, CloseWithLogging, FlushQuietly, WithClose}
3132
import org.locationtech.jts.geom.{Envelope, Geometry}
@@ -87,15 +88,17 @@ abstract class FileSystemStorage(val context: FileSystemContext, val metadata: S
8788
def getReader(query: Query, threads: Int = 1): CloseableFeatureIterator = {
8889
import org.locationtech.geomesa.index.conf.QueryHints.RichHints
8990

90-
val (filter, transform) = {
91-
val configured = QueryRunner.configureQuery(metadata.sft, query)
92-
val filter = Option(configured.getFilter).getOrElse(Filter.INCLUDE)
93-
val transform = configured.getHints.getTransform
94-
logger.debug(s"Running query '${query.getTypeName}' ${ECQL.toCQL(filter)}")
95-
logger.debug(s" Original filter: ${ECQL.toCQL(query.getFilter)}")
96-
logger.debug(s" Transforms: " + transform.fold("none") { case (t, _) => if (t.isEmpty) { "empty" } else { t }})
97-
(filter, transform)
98-
}
91+
val configured = QueryRunner.configureQuery(metadata.sft, query)
92+
val filter = Option(configured.getFilter).getOrElse(Filter.INCLUDE)
93+
val transform = configured.getHints.getTransform
94+
val sort = configured.getHints.getSortFields
95+
val max = configured.getHints.getMaxFeatures
96+
97+
logger.debug(s"Running query '${query.getTypeName}' ${ECQL.toCQL(filter)}")
98+
logger.debug(s" Original filter: ${ECQL.toCQL(query.getFilter)}")
99+
logger.debug(s" Transforms: ${transform.fold("none") { case (t, _) => if (t.isEmpty) { "empty" } else { t }}}")
100+
logger.debug(s" Sort: ${sort.fold("none") { fields => fields.map { case (f, rev) => s"$f ${if (rev) "descending" else ""}"}.mkString(", ")}}")
101+
logger.debug(s" Max features: ${max.getOrElse("none")}")
99102

100103
val files = metadata.getFiles(filter)
101104
logger.debug(s" Threading the read of ${files.size} files with $threads reader threads")
@@ -105,7 +108,10 @@ abstract class FileSystemStorage(val context: FileSystemContext, val metadata: S
105108
CloseableIterator.empty
106109
} else {
107110
val reader = createReader(Option(filter).filterNot(_ == Filter.INCLUDE), transform)
108-
FileSystemThreadedReader(reader, files, threads)
111+
val threaded = FileSystemThreadedReader(reader, files, threads)
112+
val sorted = sort.fold(threaded)(s => new SortingSimpleFeatureIterator(threaded, s))
113+
val limited = max.fold(sorted)(m => sorted.take(m))
114+
limited
109115
}
110116
}
111117

0 commit comments

Comments
 (0)