Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for redis 6/redisearch 2 #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 63 additions & 27 deletions QueryableCache.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
}
}

local.indexOptions = createObject("java", "io.redisearch.client.Client$IndexOptions").init(0);
local.indexOptions = createObject("java", "io.redisearch.client.Client$IndexOptions");
local.indexOptions.init(local.indexOptions.KEEP_FIELD_FLAGS + local.indexOptions.USE_TERM_OFFSETS);

if(len(variables.stopWords) == 0) {
local.indexOptions.setNoStopwords();
Expand All @@ -49,6 +50,15 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
local.indexOptions.setStopwords(listToArray(variables.stopWords, " "));
}

try {
local.indexDefinition = createObject("java", "io.redisearch.client.IndexDefinition")
.setPrefixes([ getName() ]);

local.indexOptions.setDefinition(local.indexDefinition);
} catch(Object e) {
// IndexDefinition isn't included in the version of JRediSearch being used
}

getClient().createIndex(
local.schema,
local.indexOptions
Expand Down Expand Up @@ -141,11 +151,11 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {

switch(local.criteria[local.i].operator) {
case "LIKE":
// strip out stop words and format the value
// fuzzy operate our normalized values
local.value = listReduce(
local.value,
function(result, item) {
if(!listFindNoCase(variables.stopWords, arguments.item, " ") && len(arguments.item) >= getMinPrefixLength()) {
if(len(arguments.item) >= getMinPrefixLength()) {
arguments.result = listAppend(arguments.result, clause & arguments.item & "*", " ");
}

Expand All @@ -167,20 +177,21 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
break;
case "IN":
case "NOT IN":
local.clause = listReduce(
local.value,
function(result, item) {
if(find(" ", arguments.item)) {
arguments.item = '"' & arguments.item & '"';
}

return listAppend(arguments.result, arguments.item, "|");
},
"",
chr(31)
);

local.clause = "(" & local.clause & ")";
local.clause = local.clause
& "("
& listReduce(
local.value,
function(result, item) {
if(find(" ", arguments.item)) {
arguments.item = '"' & arguments.item & '"';
}

return listAppend(arguments.result, arguments.item, "|");
},
"",
chr(31)
)
& ")";

if(local.criteria[local.i].operator == "NOT IN") {
local.clause = "(-" & local.clause & ")";
Expand All @@ -196,7 +207,7 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
}

// replace our placeholders w/ the cache-friendly values
local.queryString = replace(local.queryString, local.criteria[local.i].statement, local.clause, "one");
local.queryString = replace(local.queryString, local.criteria[local.i].statement, "(" & local.clause & ")", "one");
}
}

Expand Down Expand Up @@ -363,12 +374,19 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
arguments.fieldFilter,
function(result, item) {
if(document.hasProperty(arguments.item)) {
local.value = document.getString(arguments.item);
local.value = document.get(arguments.item);
if(!structKeyExists(local, "value")) {
local.value = "";
}

if(isRedisDate(arguments.item) && isNumeric(val(local.value))) {
arguments.result[arguments.item] = createObject("java", "java.util.Date").init(javaCast("long", val(local.value)));
} else if(isRedisNumeric(arguments.item) && isNumeric(val(local.value))) {
arguments.result[arguments.item] = val(local.value);
if(listFindNoCase("bigint,bit,integer,smallint,tinyint", getQueryable().getFieldSQLType(arguments.item))) {
arguments.result[arguments.item] = val(local.value);
} else {
arguments.result[arguments.item] = javaCast("double", local.value);
}
} else if(len(local.value) > 0) {
arguments.result[arguments.item] = local.value;
}
Expand All @@ -390,6 +408,10 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {
return createObject("java", "io.redisearch.client.Client").init(variables.name, variables.connectionPool.getJedisPool());
}

struct function getInfo() {
return getClient().getInfo();
}

any function getRow() {
local.document = getClient().getDocument(getRowKey(argumentCollection = arguments));

Expand Down Expand Up @@ -446,38 +468,52 @@ component accessors = "true" extends = "lib.sql.QueryableCache" {

// https://oss.redislabs.com/redisearch/Escaping.html
arguments.input = listReduce(
arguments.input,
function(result, item) {
return listAppend(arguments.result, arguments.item.REReplace("\W+", " ", "all").trim(), chr(31));
variables.normalizer.normalize(javaCast("string", arguments.input), variables.normalizerForm).replaceAll("\p{InCombiningDiacriticalMarks}+", ""),
function(outerResult, outerItem) {
arguments.outerItem = listReduce(
trim(REReplace(arguments.outerItem, "\W+", " ", "all")),
function(innerResult, innerItem) {
// these values have all been lCase'd at this point
if(!listFind(variables.stopWords, arguments.innerItem, " ")) {
arguments.innerResult = listAppend(arguments.innerResult, arguments.innerItem, " ");
}

return arguments.innerResult;
},
"",
" "
);

return listAppend(arguments.outerResult, arguments.outerItem, chr(31));
},
"",
chr(31)
);

return variables.normalizer.normalize(javaCast("string", arguments.input), variables.normalizerForm).replaceAll("\p{InCombiningDiacriticalMarks}+", "");
return trim(arguments.input);
}

void function putRow(required struct row) {
getClient().addDocument(
toRediSearchDocument(arguments.row),
createObject("java", "io.redisearch.client.AddOptions")
.setLanguage(getLanguage())
.setReplacementPolicy(createObject("java", "io.redisearch.client.AddOptions$ReplacementPolicy").PARTIAL)
.setReplacementPolicy(createObject("java", "io.redisearch.client.AddOptions$ReplacementPolicy").FULL)
);
}

void function removeRow() {
getClient().deleteDocument(javaCast("string", getRowKey(argumentCollection = arguments)), true);
}

void function seedFromQueryable(boolean overwrite = false) {
void function seedFromQueryable(boolean overwrite = false, string where = "") {
local.documents = [];
local.replacementPolicy = createObject("java", "io.redisearch.client.AddOptions$ReplacementPolicy");
local.addOptions = createObject("java", "io.redisearch.client.AddOptions")
.setLanguage(getLanguage())
.setReplacementPolicy(arguments.overwrite ? local.replacementPolicy.FULL : local.replacementPolicy.NONE);

for(local.row in getQueryable().select().execute()) {
for(local.row in getQueryable().select().where(arguments.where).execute()) {
arrayAppend(local.documents, toRediSearchDocument(local.row));

if(arrayLen(local.documents) == getImportBatchSize()) {
Expand Down
73 changes: 61 additions & 12 deletions tests/TestQueryableCache.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ component extends = "mxunit.framework.TestCase" {
variables.cache = new lib.redis.QueryableCache(connectionPool = variables.connectionPool, name = "mxunit:indexed");

variables.query = queryNew(
"id, createdTimestamp, createdDate, createdTime, foo, bar, letter",
"integer, timestamp, date, time, varchar, bit, varchar"
"id, createdTimestamp, createdDate, createdTime, foo, bar, letter, floatie",
"integer, timestamp, date, time, varchar, bit, varchar, double"
);

queryAddRow(
Expand All @@ -27,7 +27,8 @@ component extends = "mxunit.framework.TestCase" {
"createdTimestamp": parseDateTime("2019-12-12T00:00:00.123Z"),
"createdDate": createDate(2019, 12, 12),
"createdTime": createTime(0, 0, 0),
"letter": "A"
"letter": "A",
"floatie": 10000000.00
},
{
"id": 5002,
Expand All @@ -36,7 +37,8 @@ component extends = "mxunit.framework.TestCase" {
"createdTimestamp": parseDateTime("2019-12-25T00:00:00.001Z"),
"createdDate": createDate(2019, 12, 25),
"createdTime": createTime(0, 0, 0),
"letter": "B"
"letter": "B",
"floatie": 1.00
},
{
"id": 5003,
Expand All @@ -45,7 +47,8 @@ component extends = "mxunit.framework.TestCase" {
"createdTimestamp": parseDateTime("2019-12-31T00:00:00.000Z"),
"createdDate": createDate(2019, 12, 31),
"createdTime": createTime(0, 0, 0),
"letter": "C"
"letter": "C",
"floatie": -1.300001
}
]
);
Expand All @@ -57,11 +60,12 @@ component extends = "mxunit.framework.TestCase" {
{
"id": 5000 + local.i,
"bar": (!randRange(1, 3) % 2 ? local.i % 2 : javaCast("null", "")),
"foo": (local.i == 500 ? "Šťŕĭńġ" : local.i == 501 ? "the rain in spain" : createUUID()),
"foo": (local.i == 500 ? "Šťŕĭńġ" : local.i == 501 ? "the rain in spain" : local.i == 502 ? "6k" : createUUID()),
"createdTimestamp": (!randRange(1, 3) % 2 ? dateAdd("s", -(local.i), variables.now) : javaCast("null", "")),
"createdDate": (!randRange(1, 3) % 2 ? variables.now : javaCast("null", "")),
"createdTime": (!randRange(1, 3) % 2 ? variables.now : javaCast("null", "")),
"letter": chr(64 + randRange(1, 26) + (local.i % 2 ? 32 : 0))
"letter": chr(64 + randRange(1, 26) + (local.i % 2 ? 32 : 0)),
"floatie": 0.01
}
);
}
Expand All @@ -76,8 +80,6 @@ component extends = "mxunit.framework.TestCase" {

function test_0_createIndex() {
// debug(variables.exception); return;
// indexes are prefixed w/ idx:
local.queryableCache = new lib.redis.Cache(variables.connectionPool, "idx");

try{
// create the index
Expand All @@ -86,7 +88,9 @@ component extends = "mxunit.framework.TestCase" {
// index exists already, do nothing
}

assertTrue(local.queryableCache.containsKey("mxunit:indexed"));
local.info = variables.cache.getInfo();
// debug(local.info);
assertEquals("mxunit:indexed", local.info["index_name"]);
}

function test_0_seedFromQueryable() {
Expand Down Expand Up @@ -146,6 +150,15 @@ component extends = "mxunit.framework.TestCase" {
assertEquals("", local.compare.foo);
}

function test_putRow_getRow_DDMAINT_27043() {
local.queryRow = queryGetRow(variables.query, 1);
local.queryRow.createdTimestamp = "";
variables.cache.putRow(local.queryRow);
local.compare = variables.cache.getRow(argumentCollection = local.queryRow);

assertEquals("", local.compare.createdTimestamp);
}

function test_removeRow() {
local.queryRow = queryGetRow(variables.query, 1);
local.queryRow.foo = createUUID();
Expand All @@ -156,7 +169,7 @@ component extends = "mxunit.framework.TestCase" {
assertFalse(variables.cache.containsRow(id = local.queryRow.id));
}

function test_seedFromQueryable_overwrite() {
function test_seedFromQueryable_overwrite_where() {
variables.cache.seedFromQueryable();

local.row = queryGetRow(variables.query, 2);
Expand All @@ -166,7 +179,7 @@ component extends = "mxunit.framework.TestCase" {

// debug(local.row);

variables.cache.seedFromQueryable(overwrite = true);
variables.cache.seedFromQueryable(overwrite = true, where = "id = #local.row.id#");

local.overwriteElement = variables.cache.getRow(id = local.row.id);

Expand Down Expand Up @@ -299,6 +312,42 @@ component extends = "mxunit.framework.TestCase" {
debug(local.result);
}


function test_select_where_DDMAINT_27088_KEEP_FIELD_FLAGS() {
local.where = "letter = 'Šťŕĭńġ'";
local.result = variables.cache.select().where(local.where).execute();

assertEquals(0, local.result.recordCount);

debug(local.where);
debug(local.result);
}

function test_select_where_DDMAINT_27088_KEEP_FIELD_FLAGS_2() {
local.where = "foo IN (6k)";
local.result = variables.cache.select().where(local.where).execute();

assertEquals(1, local.result.recordCount);

debug(local.where);
debug(local.result);
}

function test_select_where_DDMAINT_27088_USE_TERM_OFFSETS() {
local.where = "foo = 'the rain in spain'";
local.result = variables.cache.select().where(local.where).execute();

assertEquals(1, local.result.recordCount);

debug(local.where);
debug(local.result);

local.where = "foo = 'the spain in rain'";
local.result = variables.cache.select().where(local.where).execute();

assertEquals(0, local.result.recordCount);
}

function test_select_where_in() {
local.result = variables.cache.select("id, foo").where("foo IN ('#variables.query.foo[1]#', '#variables.query.foo[2]#', '#variables.query.foo[3]#') OR id IN (5005, 5010, 5015)").execute();

Expand Down