Skip to content

Commit

Permalink
CBL-6576: Implement Partial Index LiteCore API
Browse files Browse the repository at this point in the history
Added field "where" to C4IndexOptions and implemented it.
  • Loading branch information
jianminzhao committed Jan 24, 2025
1 parent 8015487 commit bc982fd
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 2 deletions.
3 changes: 3 additions & 0 deletions C/include/c4IndexTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ typedef struct C4IndexOptions {
/** Options for vector indexes. */
C4VectorIndexOptions vector;
#endif

/** The where clause for partial indexes. Currently only Value and FullText indexes support partial index */
const char* C4NULLABLE where;
} C4IndexOptions;

/** @} */
Expand Down
32 changes: 32 additions & 0 deletions C/tests/c4QueryTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "c4Collection.h"
#include "c4Observer.h"
#include "StringUtil.hh"
#include "SQLiteDataFile.hh"
#include <thread>
using namespace std;

Expand Down Expand Up @@ -285,6 +286,37 @@ N_WAY_TEST_CASE_METHOD(C4QueryTest, "C4Query expression index", "[Query][C]") {
CHECK(run() == (vector<string>{"0000015", "0000099"}));
}

N_WAY_TEST_CASE_METHOD(C4QueryTest, "C4Query partial value index", "[Query][C]") {
C4Error err;
auto defaultColl = getCollection(db, kC4DefaultCollectionSpec);
for ( int withIndex = 0; withIndex < 2; ++withIndex ) {
if ( withIndex ) {
C4IndexOptions options{};
options.where = "gender = 'female'";
REQUIRE(c4coll_createIndex(defaultColl, C4STR("length"), c4str("length(name.first)"), kC4N1QLQuery,
kC4ValueIndex, &options, WITH_ERROR(&err)));
}
c4query_release(query);
const char* queryStr = "SELECT META().id FROM _ WHERE length(name.first) = 9 AND gender = 'female'";
query = c4query_new2(db, kC4N1QLQuery, c4str(queryStr), nullptr, ERROR_INFO(err));
REQUIRE(query);
checkExplanation(withIndex);
CHECK(run() == (vector<string>{"0000099"}));

if ( withIndex ) {
c4query_release(query);
// Logically equivalent query, changing gender = 'female' to gender != 'male'.
// Because the condition is not exact as the condition in partial index, it would not use
// the index.
const char* queryStr2 = "SELECT META().id FROM _ WHERE length(name.first) = 9 AND gender != 'male'";
query = c4query_new2(db, kC4N1QLQuery, c4str(queryStr2), nullptr, ERROR_INFO(err));
REQUIRE(query);
checkExplanation(!withIndex);
CHECK(run() == (vector<string>{"0000099"}));
}
}
}

static bool lookForIndex(C4Database* db, slice name) {
bool found = false;
auto defaultColl = C4QueryTest::getCollection(db, kC4DefaultCollectionSpec);
Expand Down
12 changes: 10 additions & 2 deletions LiteCore/Database/CollectionImpl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,16 @@ namespace litecore {
error::_throw(error::InvalidParameter, "Invalid index type");
break;
}
keyStore().createIndex(indexName, indexSpec, (QueryLanguage)indexLanguage, (IndexSpec::Type)indexType,
options);
if ( indexOptions ) {
constexpr const char* indexTypeNames[] = {"Value", "FullText", "Array", "Predictive", "Vector"};
if ( indexOptions->where && !IndexSpec::canPartialIndex((IndexSpec::Type)indexType) )
error::_throw(error::InvalidParameter, "%s index does support partial index.",
indexTypeNames[indexType]);
}

keyStore().createIndex({indexName.asString(), (IndexSpec::Type)indexType, indexSpec,
slice{indexOptions ? indexOptions->where : nullptr}, (QueryLanguage)indexLanguage,
options});
}

Retained<C4Index> getIndex(slice name) override { return C4Index::getIndex(this, name); }
Expand Down

0 comments on commit bc982fd

Please sign in to comment.