NodeJS Google数据存储库合并(联合)多组实体结果以实现OR条件

jxct1oxe  于 2023-01-01  发布在  Node.js
关注(0)|答案(3)|浏览(143)

我正在与谷歌应用引擎上的NodeJS与数据存储数据库。
由于Datastore不支持OR运算符,我需要运行多个查询并合并结果。
我计划运行多个查询,然后将结果组合到单个实体对象数组中。我已经有一个查询在运行。
问:合并数据存储区返回的两个(或多个)实体集(包括重复数据消除)的合理有效方法是什么?我认为这是集合论中的"联合"操作。
下面是基本查询大纲,它将使用一些不同的筛选器运行多次,以实现所需的OR条件。

  1. //Set requester username
  2. const requester = req.user.userName;
  3. //Create datastore query on Transfer Request kind table
  4. const task_history = datastore.createQuery('Task');
  5. //Set query conditions
  6. task_history.filter('requester', requester);
  7. //Run datastore query
  8. datastore.runQuery(task_history, function(err, entities) {
  9. if(err) {
  10. console.log('Task History JSON unable to return data results. Error message: ', err);
  11. return;
  12. //If query works and returns any entities
  13. } else if (entities[0]) {
  14. //Else if query works but does not return any entities return empty JSON response
  15. res.json(entities); //HOW TO COMBINE (UNION) MULTIPLE SETS OF ENTITIES EFFICIENTLY?
  16. return;
  17. }
  18. });

下面是我的原始帖子:Google Datastore filter with OR condition

9wbgstp7

9wbgstp71#

恕我直言,最有效的方法是在第一阶段使用Keys-only查询,然后将获得的键组合成一个列表(包括重复数据删除),然后通过简单的键查找获得实体。
仅键查询
仅键查询(一种投影查询)只返回结果实体的键,而不是实体本身,与检索整个实体相比,延迟和成本更低。
通常,首先执行仅关键字查询,然后从结果中获取实体子集比执行常规查询更经济,因为常规查询可能获取比实际需要更多的实体。
以下是创建仅限键的查询的方法:

  1. const query = datastore.createQuery()
  2. .select('__key__')
  3. .limit(1);

此方法解决了在尝试直接合并通过常规非仅键查询获得的实体列表时可能遇到的几个问题:

  • 您无法正确执行重复数据消除,因为您无法区分具有相同值的不同实体与多重查询结果中显示的同一实体
  • 通过属性值比较实体可能比较棘手,并且肯定比仅比较实体键更慢/计算成本更高
  • 如果您不能在单个请求中处理所有结果,则会因为阅读结果而实际上没有使用它们而产生不必要的数据存储成本
  • 当只处理实体键时,将实体的处理拆分为多个请求(例如,通过任务队列)要简单得多

也有一些缺点:

  • 可能会慢一点,因为您要访问数据存储区两次:一次用于键,一次用于获取实际实体
  • 你不能通过非键Map查询来获取你所需要的属性
展开查看全部
z8dt9xmd

z8dt9xmd2#

下面是我根据已接受答案中提供的建议创建的解决方案。

  1. /*History JSON*/
  2. module.exports.treqHistoryJSON = function(req, res) {
  3. if (!req.user) {
  4. req.user = {};
  5. res.json();
  6. return;
  7. }
  8. //Set Requester username
  9. const loggedin_username = req.user.userName;
  10. //Get records matching Requester OR Dataowner
  11. //Google Datastore OR Conditions are not supported
  12. //Workaround separate parallel queries get records matching Requester and Dataowner then combine results
  13. async.parallel({
  14. //Get entity keys matching Requester
  15. requesterKeys: function(callback) {
  16. getKeysOnly('TransferRequest', 'requester_username', loggedin_username, (treqs_by_requester) => {
  17. //Callback pass in response as parameter
  18. callback(null, treqs_by_requester)
  19. });
  20. },
  21. //Get entity keys matching Dataowner
  22. dataownerKeys: function(callback) {
  23. getKeysOnly('TransferRequest', 'dataowner_username', loggedin_username, (treqs_by_dataowner) => {
  24. callback(null, treqs_by_dataowner)
  25. });
  26. }
  27. }, function(err, getEntities) {
  28. if (err) {
  29. console.log('Transfer Request History JSON unable to get entity keys Transfer Request. Error message: ', err);
  30. return;
  31. } else {
  32. //Combine two arrays of entity keys into a single de-duplicated array of entity keys
  33. let entity_keys_union = unionEntityKeys(getEntities.requesterKeys, getEntities.dataownerKeys);
  34. //Get key values from entity key 'symbol' object type
  35. let entity_keys_only = entity_keys_union.map((ent) => {
  36. return ent[datastore.KEY];
  37. });
  38. //Pass in array of entity keys to get full entities
  39. datastore.get(entity_keys_only, function(err, entities) {
  40. if(err) {
  41. console.log('Transfer Request History JSON unable to lookup multiple entities by key for Transfer Request. Error message: ', err);
  42. return;
  43. //If query works and returns any entities
  44. } else {
  45. processEntitiesToDisplay(res, entities);
  46. }
  47. });
  48. }
  49. });
  50. };
  51. /*
  52. * Get keys-only entities by kind and property
  53. * @kind string name of kind
  54. * @property_type string property filtering by in query
  55. * @filter_value string of filter value to match in query
  56. * getEntitiesCallback callback to collect results
  57. */
  58. function getKeysOnly(kind, property_type, filter_value, getEntitiesCallback) {
  59. //Create datastore query
  60. const keys_query = datastore.createQuery(kind);
  61. //Set query conditions
  62. keys_query.filter(property_type, filter_value);
  63. //Select KEY only
  64. keys_query.select('__key__');
  65. datastore.runQuery(keys_query, function(err, entities) {
  66. if(err) {
  67. console.log('Get Keys Only query unable to return data results. Error message: ', err);
  68. return;
  69. } else {
  70. getEntitiesCallback(entities);
  71. }
  72. });
  73. }
  74. /*
  75. * Union two arrays of entity keys de-duplicate based on ID value
  76. * @arr1 array of entity keys
  77. * @arr2 array of entity keys
  78. */
  79. function unionEntityKeys(arr1, arr2) {
  80. //Create new array
  81. let arr3 = [];
  82. //For each element in array 1
  83. for(let i in arr1) {
  84. let shared = false;
  85. for (let j in arr2)
  86. //If ID in array 1 is same as array 2 then this is a duplicate
  87. if (arr2[j][datastore.KEY]['id'] == arr1[i][datastore.KEY]['id']) {
  88. shared = true;
  89. break;
  90. }
  91. //If IDs are not the same add element to new array
  92. if(!shared) {
  93. arr3.push(arr1[i])
  94. }
  95. }
  96. //Concat array 2 and new array 3
  97. arr3 = arr3.concat(arr2);
  98. return arr3;
  99. }
展开查看全部
lf5gs5x2

lf5gs5x23#

我只是想写信给那些偶然发现这个的人...
There is a workaround for some cases of not having the OR operator if you can restructure your data a bit, using Array properties: https://cloud.google.com/datastore/docs/concepts/entities#array_properties
来自文档:
例如,在使用等式过滤器执行查询时,数组属性可能会很有用:如果实体的任何属性值与过滤器中指定的值匹配,则实体满足查询
因此,如果您需要查询具有多个潜在值之一的所有实体,将每个实体的所有可能性放入Array属性,然后为查询索引它,应该会产生您想要的结果。(但是,您需要维护该附加属性,或者如果该Array实现可以满足您的所有需要,则用该Array实现替换现有属性。)

相关问题