I need help. I have already spent a long time to improve my request exec time.
I can’t get time under 9s.
Maybe my indexes are not good or my request has been not writted correctly.
SELECT ARRAY_AGG(META(categories).id)[0] AS _ID, ARRAY_AGG(META(categories).cas)[0] AS _CAS, ARRAY_DISTINCT(ARRAY_FLATTEN(ARRAY_AGG(productIds.`COUNTRY`), 1)) AS productIds
FROM `test-data` AS categories
LET visibleChildren = (
SELECT RAW COUNT(*)
FROM `test-data` AS childCategories USE KEYS categories.childIds
WHERE childCategories.origin = "COUNTRY" OR (childCategories.origin = "GENERAL" AND NOT ARRAY_CONTAINS(childCategories.deletedFor, "COUNTRY"))
)[0]
WHERE categories._class = "com.test.category.Category"
AND visibleChildren = 0
AND ANY p IN OBJECT_PAIRS(categories.productIds) SATISFIES p.name = "COUNTRY" AND ARRAY_COUNT(p.val) <> 0 END
AND ( ( categories.origin = "GENERAL" AND categories.metadata.configurations.GENERAL.enabled = true ) OR ( categories.origin = "COUNTRY" ))
AND ANY conf IN OBJECT_PAIRS(categories.metadata.configurations) SATISFIES [conf.name, conf.val.enabled] = ["COUNTRY", false] END
AND (NOT ARRAY_CONTAINS(deletedFor, "COUNTRY") OR categories.deletedFor IS NOT VALUED)
My indexes are:
CREATE INDEX `idx_1`
ON `test-data`(origin, metadata.configurations.GENERAL.enabled, DISTINCT ARRAY [conf.name, conf.val.enabled] FOR conf IN OBJECT_PAIRS(metadata.configurations) END, deletedFor, childIds)
WHERE _class = "com.test.category.Category"
USING GSI;
CREATE INDEX idx_2 ON `test-data`(DISTINCT ARRAY ARRAY_COUNT(p.val) FOR p IN OBJECT_PAIRS(productIds) END, origin)
WHERE _class = "com.test.category.Category"
USING GSI;
MY model is tree based one and I use couchbase version 5.1.
The productIds can hold more than 1000 elements per COUNTRY (1, 2, 3 …)
Origin various values are GENERAL, COUNTRY1, COUNTRY2, …, COUNTRY30 so the productIds and configurations can have as many keys than origins.
In this request, GENERAL is constant and not COUNTRY
SELECT ARRAY_AGG(META(categories).id)[0] AS _ID, ARRAY_AGG(META(categories).cas)[0] AS _CAS, ARRAY_DISTINCT(ARRAY_FLATTEN(ARRAY_AGG(productIds.`US`), 1)) AS productIds
FROM `test-data` AS categories
LET visibleChildren = (
SELECT RAW COUNT(*)
FROM `test-data` AS childCategories USE KEYS categories.childIds
WHERE childCategories.origin = "US" OR (childCategories.origin = "GENERAL" AND NOT ARRAY_CONTAINS(childCategories.deletedFor, "US"))
)[0]
WHERE categories._class = "com.test.category.Category"
AND visibleChildren = 0
AND ANY p IN OBJECT_PAIRS(categories.productIds) SATISFIES p.name = "US" AND ARRAY_COUNT(p.val) <> 0 END
AND ( ( categories.origin = "GENERAL" AND categories.metadata.configurations.`GENERAL`.enabled = true ) OR ( categories.origin = "US" ))
AND ANY conf IN OBJECT_PAIRS(categories.metadata.configurations) SATISFIES [conf.name, conf.val.enabled] = ["US", false] END
AND (NOT ARRAY_CONTAINS(deletedFor, "US") OR categories.deletedFor IS NOT VALUED
I am not sure if this will work in 5.1 but you can try it
CREATE INDEX ix1 ON `test-data` (origin,
DISTINCT ARRAY [p.name, ARRAY_COUNT(p.val) > 0, metadata.configurations.[p.name].enabled,
ARRAY_CONTAINS(IFMISSINGORNULL(deletedFor, []) ,p.name)]
FOR p IN OBJECT_PAIRS(productIds) END,
metadata.configurations.`GENERAL`.enabled
) WHERE _class = "com.test.category.Category";
SELECT ARRAY_AGG(META(c).id)[0] AS _ID,
ARRAY_AGG(META(c).cas)[0] AS _CAS,
ARRAY_DISTINCT(ARRAY_FLATTEN(ARRAY_AGG(c.productIds.`US`), 1)) AS productIds
FROM `test-data` AS c USE INDEX (ix1)
LET visibleChildren = (
SELECT RAW COUNT(1)
FROM `test-data` AS cc USE KEYS c.childIds
WHERE cc.origin = "US" OR (cc.origin = "GENERAL" AND NOT ARRAY_CONTAINS(cc.deletedFor, "US"))
)[0]
WHERE c._class = "com.test.category.Category"
AND ((c.origin = "GENERAL" AND c.metadata.configurations.`GENERAL`.enabled = true ) OR ( c.origin = "US" ))
AND visibleChildren = 0
AND ANY p IN OBJECT_PAIRS(c.productIds)
SATISFIES [p.name, ARRAY_COUNT(p.val) > 0, c.metadata.configurations.[p.name].enabled,
ARRAY_CONTAINS(IFMISSINGORNULL(c.deletedFor, []) ,p.name)] = ["US", true, false, false] END;
The query is complex and have limited options. If 6.0.0 can think few more improvements.
Try the following if helps anything further. If you are not interested _ID, _CAS values from same document, you can use MAX or MIN instead of ARRAY_AGG() which is costly to form array if lot of rows.
CREATE INDEX ix2 ON `test-data` (origin,
DISTINCT ARRAY [p.name, ARRAY_COUNT(p.val) > 0, metadata.configurations.[p.name].enabled,
ARRAY_CONTAINS(IFMISSINGORNULL(deletedFor, []) ,p.name)]
FOR p IN OBJECT_PAIRS(productIds) END,
metadata.configurations.`GENERAL`.enabled, childIds
) WHERE _class = "com.test.category.Category";
SELECT ARRAY_AGG(META(c).id)[0] AS _ID,
ARRAY_AGG(META(c).cas)[0] AS _CAS,
ARRAY_DISTINCT(ARRAY_FLATTEN(ARRAY_AGG(c.productIds.`US`), 1)) AS productIds
FROM ( SELECT RAW c.id
FROM ( SELECT META(c).id, c.childIds
FROM `test-data` AS c USE INDEX (ix2)
WHERE c._class = "com.test.category.Category"
AND c.origin = "GENERAL" AND c.metadata.configurations.`GENERAL`.enabled = true
AND ANY p IN OBJECT_PAIRS(c.productIds)
SATISFIES [p.name, ARRAY_COUNT(p.val) > 0, c.metadata.configurations.[p.name].enabled,
ARRAY_CONTAINS(IFMISSINGORNULL(c.deletedFor, []) ,p.name)] = ["US", true, false, false] END
UNION
SELECT META(c).id, c.childIds
FROM `test-data` AS c USE INDEX (ix2)
WHERE c._class = "com.test.category.Category"
AND c.origin = "US"
AND ANY p IN OBJECT_PAIRS(c.productIds)
SATISFIES [p.name, ARRAY_COUNT(p.val) > 0, c.metadata.configurations.[p.name].enabled,
ARRAY_CONTAINS(IFMISSINGORNULL(c.deletedFor, []) ,p.name)] = ["US", true, false, false] END) AS c
LET visibleChildren = ( SELECT RAW COUNT(1)
FROM `test-data` AS cc USE KEYS c.childIds
WHERE cc.origin = "US" OR (cc.origin = "GENERAL"
AND NOT ARRAY_CONTAINS(cc.deletedFor, "US"))
)[0]
WHERE visibleChildren == 0 ) AS ckeys
JOIN `test-data` AS c ON KEYS ckeys;