2017-11-22 1 views
0

Ich arbeite mit NodeJS auf Google App Engine mit der Datastore-Datenbank.Google Datastore kombinieren (Vereinigung) mehrere Sätze von Entity-Ergebnissen, um OR-Bedingung zu erreichen

Aufgrund der Tatsache, dass Datastore does not have support the OR operator, muss ich mehrere Abfragen ausführen und die Ergebnisse kombinieren.

Ich plane, mehrere Abfragen auszuführen und dann die Ergebnisse in einem einzigen Array von Entitätsobjekten zu kombinieren. Ich habe eine einzige Abfrage, die bereits funktioniert.

Frage: Was ist eine recht effiziente Methode zum Kombinieren von zwei (oder mehr) Entitätsgruppen, die von Datastore zurückgegeben werden, einschließlich Deduplizierung? Ich glaube, das wäre eine "Union" -Operation im Sinne der Mengenlehre.

Hier ist die grundlegende Abfrage Gliederung, die mehrmals mit einigen verschiedenen Filtern ausgeführt werden, um die erforderlichen OR-Bedingungen zu erreichen.

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

Hier ist mein Original-Beitrag: Google Datastore filter with OR condition

Antwort

1

IMHO die effizienteste Art und Weise Keys-only-Abfragen in der 1. Stufe zu verwenden wäre, führt dann die Kombination der in eine einzige Liste erhielten Tasten (einschließlich Deduplizierung), gefolgt von Erlangen der Entitäten einfach durch Schlüsselsuche. Von Projection queries:

Keys-only-Abfragen

Ein Schlüssel-only-Abfrage (das ist eine Art von Projektions Abfrage) gibt nur die Schlüssel der Ergebnis Einheiten anstelle der Einheiten selbst bei niedriger Latenz und Kosten als das Abrufen von ganzen Entitäten.

Es ist oft wirtschaftlicher zuerst eine Schlüssel-Abfrage nur zu tun, und dann eine Teilmenge von Entitäten aus den Ergebnissen holen, anstatt eine allgemeine Abfrage ausgeführt, die mehr Einheiten holen können, als Sie tatsächlich benötigen.

Hier ist, wie ein Schlüssel-only Abfrage erstellen:

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

Diese Methode einige Probleme behebt, die auftreten können, wenn sie versuchen direkt Listen der Stellen durch regelmäßige, nicht-Tasten-nur erhalten kombinieren Anfragen:

  • Sie können nicht Deduplizierung richtig, weil Sie nicht den Unterschied zwischen verschiedenen Einheiten mit identischen Werten und die gleiche Einheit erscheinen in mehrfach Abfrageergebnisse
  • sagen kann
  • Vergleichen von Entitäten nach Eigenschaftswerten kann schwierig sein und ist definitiv langsamer/teurer als Vergleichen nur Entitätsschlüssel
  • Wenn Sie nicht alle Ergebnisse in einer einzigen Anfrage verarbeiten können, entstehen unnötige Datenspeicherkosten für das Lesen ohne mit ihnen tatsächlich
  • es ist viel einfacher Verarbeitung von Entitäten in mehreren Anfragen aufzuteilen (via Task-Warteschlangen, zum Beispiel), wenn

es gibt einige Nachteile genausogut Entitätsschlüsseln Handhabung:

  • Es kann ein bisschen langsamer sein, weil Sie zweimal in den Datenspeicher gehen: einmal für die Schlüssel und einmal, um die tatsächlichen Entitäten
  • zu bekommen, können Sie nicht den Vorteil nutzen, nur die Eigenschaften zu bekommen, die Sie über Nicht-Schlüssel-nur benötigen Projektionsabfragen
0

Hier ist die Lösung, die ich basierend auf den Ratschlägen in der angenommenen Antwort erstellt.

/*History JSON*/ 
module.exports.treqHistoryJSON = function(req, res) { 
    if (!req.user) { 
    req.user = {}; 
    res.json(); 
    return; 
    } 

    //Set Requester username 
    const loggedin_username = req.user.userName; 

    //Get records matching Requester OR Dataowner 
    //Google Datastore OR Conditions are not supported 
    //Workaround separate parallel queries get records matching Requester and Dataowner then combine results 
    async.parallel({ 
    //Get entity keys matching Requester 
    requesterKeys: function(callback) { 
     getKeysOnly('TransferRequest', 'requester_username', loggedin_username, (treqs_by_requester) => { 
     //Callback pass in response as parameter 
     callback(null, treqs_by_requester) 
     }); 
    }, 
    //Get entity keys matching Dataowner 
    dataownerKeys: function(callback) { 
     getKeysOnly('TransferRequest', 'dataowner_username', loggedin_username, (treqs_by_dataowner) => { 
     callback(null, treqs_by_dataowner) 
     }); 
    } 
    }, function(err, getEntities) { 
    if (err) { 
     console.log('Transfer Request History JSON unable to get entity keys Transfer Request. Error message: ', err); 
     return; 
    } else { 
     //Combine two arrays of entity keys into a single de-duplicated array of entity keys 
     let entity_keys_union = unionEntityKeys(getEntities.requesterKeys, getEntities.dataownerKeys); 
     //Get key values from entity key 'symbol' object type 
     let entity_keys_only = entity_keys_union.map((ent) => { 
     return ent[datastore.KEY]; 
     }); 
     //Pass in array of entity keys to get full entities 
     datastore.get(entity_keys_only, function(err, entities) { 
     if(err) { 
      console.log('Transfer Request History JSON unable to lookup multiple entities by key for Transfer Request. Error message: ', err); 
      return; 
      //If query works and returns any entities 
     } else { 
      processEntitiesToDisplay(res, entities); 
     } 
     }); 
    } 
    }); 

}; 

/* 
* Get keys-only entities by kind and property 
* @kind string name of kind 
* @property_type string property filtering by in query 
* @filter_value string of filter value to match in query 
* getEntitiesCallback callback to collect results 
*/ 
function getKeysOnly(kind, property_type, filter_value, getEntitiesCallback) { 
    //Create datastore query 
    const keys_query = datastore.createQuery(kind); 
    //Set query conditions 
    keys_query.filter(property_type, filter_value); 
    //Select KEY only 
    keys_query.select('__key__'); 
    datastore.runQuery(keys_query, function(err, entities) { 
    if(err) { 
     console.log('Get Keys Only query unable to return data results. Error message: ', err); 
     return; 
    } else { 
     getEntitiesCallback(entities); 
    } 
    }); 
} 

/* 
* Union two arrays of entity keys de-duplicate based on ID value 
* @arr1 array of entity keys 
* @arr2 array of entity keys 
*/ 
function unionEntityKeys(arr1, arr2) { 
    //Create new array 
    let arr3 = []; 
    //For each element in array 1 
    for(let i in arr1) { 
    let shared = false; 
     for (let j in arr2) 
     //If ID in array 1 is same as array 2 then this is a duplicate 
     if (arr2[j][datastore.KEY]['id'] == arr1[i][datastore.KEY]['id']) { 
      shared = true; 
      break; 
     } 
     //If IDs are not the same add element to new array 
     if(!shared) { 
     arr3.push(arr1[i]) 
     } 
    } 
    //Concat array 2 and new array 3 
    arr3 = arr3.concat(arr2); 
    return arr3; 
} 
Verwandte Themen