From 5204ab54301defc14d3fd03c2cefb44a928a796a Mon Sep 17 00:00:00 2001 From: Andrea Di Cesare Date: Wed, 8 Jan 2025 12:02:56 +0100 Subject: [PATCH] :sparkles: Allow to specify hint index by name See https://restheart.org/docs/mongodb-rest/read-docs#hint --- .../org/restheart/exchange/MongoRequest.java | 25 +++++++------- .../test/integration/GetIndexesIT.java | 12 +++---- .../org/restheart/mongodb/db/Collections.java | 34 +++++++++++++------ .../org/restheart/mongodb/db/Databases.java | 12 +++---- .../mongodb/db/GetCollectionCacheKey.java | 19 ++++++----- .../collection/GetCollectionHandler.java | 2 +- 6 files changed, 60 insertions(+), 44 deletions(-) diff --git a/commons/src/main/java/org/restheart/exchange/MongoRequest.java b/commons/src/main/java/org/restheart/exchange/MongoRequest.java index e4c85faff..16dc3d011 100644 --- a/commons/src/main/java/org/restheart/exchange/MongoRequest.java +++ b/commons/src/main/java/org/restheart/exchange/MongoRequest.java @@ -31,7 +31,6 @@ import org.bson.BsonArray; import org.bson.BsonDocument; -import org.bson.BsonInt32; import org.bson.BsonInvalidOperationException; import org.bson.BsonString; import org.bson.BsonValue; @@ -74,6 +73,7 @@ import static org.restheart.exchange.ExchangeKeys._TRANSACTIONS; import org.restheart.mongodb.RSOps; import org.restheart.mongodb.db.sessions.ClientSessionImpl; +import static org.restheart.utils.BsonUtils.array; import static org.restheart.utils.BsonUtils.document; import org.restheart.utils.MongoServiceAttachments; import org.restheart.utils.URLUtils; @@ -883,35 +883,36 @@ public BsonDocument getSortByDocument() throws JsonParseException { } /** - * - * @return @throws JsonParseException + * hints can be either an index document as {"key":1} or an index name + * the compact format is allowed, eg. +key means {"key":1} + * if the value is a string (not starting with + or -) than it is taken into account as an index name + * @return an array of hints, either documents or strings (index names) */ - public BsonDocument getHintDocument() throws JsonParseException { + public BsonArray getHintValue() { if (hint == null || hint.isEmpty()) { return null; } else { - var ret = new BsonDocument(); + var ret = array(); + hint.stream().forEach(s -> { var _s = s.strip(); // the + sign is decoded into a space, in case remove it // manage the case where hint is a json object try { - var _hint = BsonDocument.parse(_s); - - ret.putAll(_hint); + ret.add(BsonDocument.parse(_s)); } catch (JsonParseException e) { // ret is just a string, i.e. an index name if (_s.startsWith("-")) { - ret.put(_s.substring(1), new BsonInt32(-1)); + ret.add(document().put(_s.substring(1), -1)); } else if (_s.startsWith("+")) { - ret.put(_s.substring(1), new BsonInt32(11)); + ret.add(document().put(_s.substring(1), 1)); } else { - ret.put(_s, new BsonInt32(1)); + ret.add(_s); } } }); - return ret; + return ret.get(); } } diff --git a/core/src/test/java/org/restheart/test/integration/GetIndexesIT.java b/core/src/test/java/org/restheart/test/integration/GetIndexesIT.java index 6a02d6b80..ea3850095 100644 --- a/core/src/test/java/org/restheart/test/integration/GetIndexesIT.java +++ b/core/src/test/java/org/restheart/test/integration/GetIndexesIT.java @@ -20,11 +20,6 @@ */ package org.restheart.test.integration; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - import java.net.URI; import org.apache.http.HttpEntity; @@ -33,6 +28,10 @@ import org.apache.http.client.fluent.Request; import org.apache.http.client.fluent.Response; import org.apache.http.util.EntityUtils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.restheart.exchange.Exchange; @@ -42,6 +41,7 @@ import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; + import kong.unirest.Unirest; /** @@ -211,7 +211,7 @@ public void createTestData() throws Exception { @SuppressWarnings("rawtypes") public void testGetHintStringFormat() throws Exception { kong.unirest.HttpResponse resp = Unirest.get(url(DB, COLL)) - .queryString("hint", "a") + .queryString("hint", "+a") .queryString("sort", "{}") .basicAuth(ADMIN_ID, ADMIN_PWD) .asString(); diff --git a/mongodb/src/main/java/org/restheart/mongodb/db/Collections.java b/mongodb/src/main/java/org/restheart/mongodb/db/Collections.java index 8f256666d..020a897f7 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/db/Collections.java +++ b/mongodb/src/main/java/org/restheart/mongodb/db/Collections.java @@ -166,18 +166,29 @@ FindIterable findIterable( final MongoCollection coll, final BsonDocument sortBy, final BsonDocument filters, - final BsonDocument hint, + final BsonArray hints, final BsonDocument keys, final int batchSize) throws JsonParseException { var ret = cs.isPresent() ? coll.find(cs.get(), filters) : coll.find(filters); - return ret.projection(keys) + var find = ret.projection(keys) .sort(sortBy) .batchSize(batchSize) - .hint(hint) .maxTime(MongoServiceConfiguration.get().getQueryTimeLimit(), TimeUnit.MILLISECONDS); + + if (hints != null) { + for (var hint : hints) { + find = switch(hint) { + case BsonString hintStr -> find.hintString(hintStr.getValue()); + case BsonDocument hintDoc -> find.hint(hintDoc); + default -> find; + }; + } + } + + return find; } /** @@ -190,7 +201,7 @@ FindIterable findIterable( * @param pagesize * @param sortBy * @param filter - * @param hint + * @param hints * @param keys * @param cursorAllocationPolicy * @return the documents in the collection as a BsonArray @@ -204,7 +215,7 @@ BsonArray getCollectionData( final int pagesize, final BsonDocument sortBy, final BsonDocument filters, - final BsonDocument hint, + final BsonArray hints, final BsonDocument keys, final boolean useCache) throws JsonParseException { @@ -212,15 +223,15 @@ BsonArray getCollectionData( var ret = new BsonArray(); if (!useCache) { - return getCollectionDataFromDb(cs, coll, rsOps, dbName, collName, page, pagesize, sortBy, filters, hint, keys, useCache); + return getCollectionDataFromDb(cs, coll, rsOps, dbName, collName, page, pagesize, sortBy, filters, hints, keys, useCache); } else { var from = pagesize * (page - 1); var to = from + pagesize; - var match = GetCollectionCache.getInstance().find(new GetCollectionCacheKey(cs, coll, sortBy, filters, hint, keys, from, to, 0, false)); + var match = GetCollectionCache.getInstance().find(new GetCollectionCacheKey(cs, coll, sortBy, filters, keys, hints, from, to, 0, false)); if (match == null) { - return getCollectionDataFromDb(cs, coll, rsOps, dbName, collName, page, pagesize, sortBy, filters, hint, keys, useCache); + return getCollectionDataFromDb(cs, coll, rsOps, dbName, collName, page, pagesize, sortBy, filters, hints, keys, useCache); } else { var maxToIndex = match.getKey().to() - match.getKey().from(); var fromIndex = from - match.getKey().from(); @@ -279,7 +290,7 @@ private BsonArray getCollectionDataFromDb(final Optional cs, final int pagesize, final BsonDocument sortBy, final BsonDocument filters, - final BsonDocument hint, + final BsonArray hints, final BsonDocument keys, final boolean useCache) { var ret = new BsonArray(); @@ -287,7 +298,7 @@ private BsonArray getCollectionDataFromDb(final Optional cs, var batchSize = useCache ? GET_COLLECTION_CACHE_BATCH_SIZE : pagesize; - try (var cursor = findIterable(cs, coll, sortBy, filters, hint, keys, batchSize).skip(from).cursor()) { + try (var cursor = findIterable(cs, coll, sortBy, filters, hints, keys, batchSize).skip(from).cursor()) { int added = 0; while(added < pagesize) { var next = cursor.tryNext(); @@ -304,7 +315,8 @@ private BsonArray getCollectionDataFromDb(final Optional cs, var count = cursorCount(cursor); var to = from + count; var exhausted = count < GET_COLLECTION_CACHE_BATCH_SIZE; - var newkey = new GetCollectionCacheKey(cs, coll, sortBy, filters, hint, keys, from, to, System.nanoTime(), exhausted); + + var newkey = new GetCollectionCacheKey(cs, coll, sortBy, filters, keys, hints, from, to, System.nanoTime(), exhausted); LOGGER.debug("{} entry in collection cache: {}", ansi().fg(YELLOW).bold().a("new").reset().toString(), newkey); var cursorDocs = cursorDocs(cursor); diff --git a/mongodb/src/main/java/org/restheart/mongodb/db/Databases.java b/mongodb/src/main/java/org/restheart/mongodb/db/Databases.java index ad83b66ed..956988ab7 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/db/Databases.java +++ b/mongodb/src/main/java/org/restheart/mongodb/db/Databases.java @@ -476,7 +476,7 @@ public long getCollectionSize(final Optional cs, Optional * @param pagesize * @param sortBy * @param filters - * @param hint + * @param hints * @param keys * @param useCache * @return the documents in the collection as a BsonArray @@ -490,11 +490,11 @@ public BsonArray getCollectionData( final int pagesize, final BsonDocument sortBy, final BsonDocument filters, - final BsonDocument hint, + final BsonArray hints, final BsonDocument keys, final boolean useCache) throws JsonParseException { - return collections.getCollectionData(cs, rsOps, dbName, collName, page, pagesize, sortBy, filters, hint, keys, useCache); + return collections.getCollectionData(cs, rsOps, dbName, collName, page, pagesize, sortBy, filters, hints, keys, useCache); } /** @@ -600,7 +600,7 @@ public List getCollectionIndexes( * @param collName the collection name * @param sortBy * @param filters - * @param hint + * @param hints * @param keys * @param batchSize * @return the FindIterable @@ -612,7 +612,7 @@ public FindIterable findIterable( final String collName, final BsonDocument sortBy, final BsonDocument filters, - final BsonDocument hint, + final BsonArray hints, final BsonDocument keys, final int batchSize) { return collections.findIterable( @@ -620,7 +620,7 @@ public FindIterable findIterable( collections.collection(rsOps, dbName, collName), sortBy, filters, - hint, + hints, keys, batchSize); } diff --git a/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCacheKey.java b/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCacheKey.java index 592b92c1a..803b47bdf 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCacheKey.java +++ b/mongodb/src/main/java/org/restheart/mongodb/db/GetCollectionCacheKey.java @@ -20,12 +20,15 @@ */ package org.restheart.mongodb.db; -import com.mongodb.client.ClientSession; -import com.mongodb.client.MongoCollection; import java.util.Formatter; import java.util.Optional; +import org.bson.BsonArray; import org.bson.BsonDocument; +import org.restheart.utils.BsonUtils; + +import com.mongodb.client.ClientSession; +import com.mongodb.client.MongoCollection; /** * @@ -37,7 +40,7 @@ public record GetCollectionCacheKey( BsonDocument sort, BsonDocument filter, BsonDocument keys, - BsonDocument hint, + BsonArray hints, int from, int to, long cursorId, @@ -50,10 +53,10 @@ public static GetCollectionCacheKey clone(GetCollectionCacheKey key) { return new GetCollectionCacheKey( key.session, key.collection, + key.sort, key.filter, key.keys, - key.hint, - key.sort, + key.hints, key.from, key.to, key.cursorId, @@ -66,7 +69,7 @@ String getCacheStatsGroup() { + " - " + (sort == null ? "no sort" : sort.toString()) + " - " - + (hint == null ? "no hint" : hint.toString()) + + (hints == null ? "no hints" : BsonUtils.toJson(hints)) + " - " + f.format("%10d", from) + " - " @@ -77,13 +80,13 @@ String getCacheStatsGroup() { @Override public String toString() { return String.format( - "[session=%s, collection=%s, sort=%s, filter=%s, keys=%s, hint=%s, from=%s, to=%s, cursorId=%s, exhausted=%s]", + "[session=%s, collection=%s, sort=%s, filter=%s, keys=%s, hints=%s, from=%s, to=%s, cursorId=%s, exhausted=%s]", session, collection.getNamespace(), sort, filter, keys, - hint, + hints, from, to, cursorId, diff --git a/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java b/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java index 740bb6b06..bfff1f6dd 100644 --- a/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java +++ b/mongodb/src/main/java/org/restheart/mongodb/handlers/collection/GetCollectionHandler.java @@ -126,7 +126,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception { request.getPagesize(), sort, filter, - request.getHintDocument(), + request.getHintValue(), request.getProjectionDocument(), request.isCache() && isGetCollectionCacheEnabled); }