Snaplogic: How to join two tables with a condition other than equals (like, >, <) - snaplogic

I am trying to join two tables in which column from table 1 "contains" data from one of the columns in table 2 i.e. consider below hypothetical:
Table error_log:
id| description
1 | this is right, bla bla, bla
2 | this is , bla bla, bla, wrong
3 | this is , bla bla, bla, a disaster, , bla bla, bla
4 | bla, bla, bla
Table result_type:
id|type
1|Right
2|Wrong
3|Disaster
Now, I wish to join these two tables and store the results in 3rd table i.e.
Table analysis:
id|error_log_id|result_type_id
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
4 | 4 | null
Normally, in any RDBMS, I can do this pretty easily with a left join with a like condition i.e.
select e.error_log_id, r.result_type_id from error_log e
left join result_type r on e.description like '%'+ r.type +'%'
but I can't seem to find to do so through snaplogic? I tried Snap Join, but it only provides equals join condition i.e.
any suggestion is highly appreciated.

Assuming that the number of records in result_type doesn't change that often and is a lot smaller than the number of records in error_log, you could use In-memory Lookup snap instead of the Join snap. That aside, there is no straight forward way of doing this using regular snaps. I would suggest doing complicated queries in the database whenever possible. Following is an implementation using the Script snap.
Sample Pipeline
In-memory Lookup
Add a field called join_id (or whatever) with the value 1 (or whatever) and join based on this field in the lookup. This will add the whole lookup (in your case, the types) in all the incoming documents.
Script
try { load("nashorn:mozilla_compat.js"); } catch(e) { }
importPackage(com.snaplogic.scripting.language);
importClass(java.util.ArrayList);
importClass(java.util.LinkedHashMap);
var impl = {
input : input,
output : output,
error : error,
log : log,
execute : function () {
this.log.info("Executing Transform Script");
while (this.input.hasNext()) {
try {
var inDoc = this.input.next();
var outDocs = new ArrayList();
var errorLogId = inDoc.id;
var description = inDoc.description;
var types = inDoc.types;
var flag = false;
for(var idx in types) {
var type = types[idx];
if(description.toLowerCase().contains(type.type.toLowerCase())) {
var outDoc = new LinkedHashMap();
outDoc.put('error_log_id', errorLogId);
outDoc.put('result_type_id', type.id);
outDocs.add(outDoc);
}
}
if(outDocs.isEmpty()) {
var outDoc = new LinkedHashMap();
outDoc.put('error_log_id', errorLogId);
outDoc.put('result_type_id', null);
outDocs.add(outDoc);
}
for(var idx in outDocs) {
var outDoc = outDocs[idx];
this.output.write(inDoc, outDoc);
}
}
catch (err) {
var errDoc = new LinkedHashMap();
errDoc.put("error", err);
this.log.error(err);
this.error.write(errDoc);
}
}
this.log.info("Script executed");
},
cleanup : function () {
this.log.info("Cleaning up")
}
};
var hook = new com.snaplogic.scripting.language.ScriptHook(impl);
Output

That is the correct snap, try changing the ‘Join type’ to the relevant value, such as Outer.

Related

how to test API query string for correctness in TypeScript / Nodejs

I have build an API using Nodejs and express which listens to a query string like so:
http://localhost:3000/get?pos=52.4919643,13.3772875&bboxSize=400&query=wfs&outputType=geojson
I need all of these arguments to work. what would be the correct way to test a string like that for correctness? Currently I try something like this:
type OutputType = "glb" | "cityjson" | "geojson"
type QueryType = "lod2" | "wfs"
let lat: number
let lon: number
let bboxSize: number
let output: OutputType
let query: QueryType
//try to get all necessary arguments for running the api
//TODO: codereview?
try {
lat = Number((request.query.pos as string).split(",")[0])
lon = Number((request.query.pos as string).split(",")[1])
bboxSize = isFinite(Number(request.query.bboxSize as string)) ? Number((request.query.bboxSize as string)) : 200
output = request.query.outputType as OutputType
query = request.query.query as QueryType
if (bboxSize > maxOutputSize) {
throw "bbox Size too big";
}
if (!isFinite(lat) || !isFinite(lon) || !isFinite(bboxSize)) {
throw "lat lon bboxSize need to be numbers";
}
if (query === "wfs" && output !== "geojson") {
throw "only geojson is allowed for wfs data";
}
}
catch (err) {
return response.status(400).json(`Error in Query`)
}
it works, but how can I be sure that this really works? Would I go for a regex (it would be super hard to maintain I guess..)? or is there a "standard" how to check strings for correctness?
I am concerned about the developer experience (changing the string would need to be reflected in the test too in a simple matter..) and of course about the fact that I could have forgotten a case where it does not catch an error...
Any idea would be great!
Cheers

Trying to update value in firebase database while listening for changes on another place (Cloud Functions)

I am trying to update the count of the participants in a field in firebase realtime database.
My database looks like this:
root
|_course
| |_Course1
| |_descr: "some text"
| |_lect: "someUID"
|_groups
| |_Course1
| |_Group1
| |_current: 5
| |_others -//-
|_participation
| |_Course1
| |_someUID: "Group1"
So now I'm trying to listen for writings on participation/Course1/someUID and when some record shows up I want to update the 'current' field to 6 in groups/Course1/Group1.
So far I've come with:
exports.afterGroupJoined = functions.database.ref('/participation/{courseId}/{uid}').onWrite((change, context) => {
const writtenContent = change.after.val(); // groupId
const courseId = context.params.courseId;
const uid = context.auth ? context.auth.uid : null;
admin.database().ref('/groups/' + courseId + '/' + writtenContent).once('value').then(snapshot => {
const count = snapshot.val();
const current = count+1;
console.log('User ' + uid + ' joined group ' + writtenContent + ' in course ' + courseId);
const promise1 = admin.database().ref('/groups/' + courseId + '/' + writtenContent + '/current').set(parseInt(current));
return Promise.all([promise1]);
}).catch(err => {
console.log(err);
});
});
But what I get is a new child in groups/Course1/Group1 named 'null' with child 'current' having value '0':
root
|_groups
| |_Course1
| |_Group1
| | |_current: 5
| | |_others -//-
| |_null
| |_current: 0
The value of 'current' should've been 6, but I get a new child.
This is from the log:
User fNhAVZUo9QeSbYt0TwBIQmL2mYq1 joined group Group1 in course Course1
Error: Reference.set failed: First argument contains NaN in property 'groups.Course1.Group1.current' at validateFirebaseData
Any help is appreciated.
It seems like you're reading this JSON:
"Group1": {
"current": 5
}
Which means you need this code to get the actual count:
const count = snapshot.val().current;
But you're not using anything else from the group, so you might as well just read the count itself. The Promise.all() call also seems unneeded, since you have only one promise. So the entire block could be:
let ref = admin.database().ref('/groups/' + courseId + '/' + writtenContent+'/current');
ref.once('value').then(snapshot => {
const count = snapshot.val();
return ref.set(count+1);
}).catch(err => {
console.log(err);
});
Finally: this approach is susceptible to race conditions if multiple users run the above at almost the same time. You might want to consider using a transaction.

Delete Documents from CosmosDB based on condition through Query Explorer

What's the query or some other quick way to delete all the documents matching the where condition in a collection?
I want something like DELETE * FROM c WHERE c.DocumentType = 'EULA' but, apparently, it doesn't work.
Note: I'm not looking for any C# implementation for this.
This is a bit old but just had the same requirement and found a concrete example of what #Gaurav Mantri wrote about.
The stored procedure script is here:
https://social.msdn.microsoft.com/Forums/azure/en-US/ec9aa862-0516-47af-badd-dad8a4789dd8/delete-multiple-docdb-documents-within-the-azure-portal?forum=AzureDocumentDB
Go to the Azure portal, grab the script from above and make a new stored procedure in the database->collection you need to delete from.
Then right at the bottom of the stored procedure pane, underneath the script textarea is a place to put in the parameter. In my case I just want to delete all so I used:
SELECT c._self FROM c
I guess yours would be:
SELECT c._self FROM c WHERE c.DocumentType = 'EULA'
Then hit 'Save and Execute'. Viola, some documents get deleted. After I got it working in the Azure Portal I switched over the Azure DocumentDB Studio and got a better view of what was happening. I.e. I could see I was throttled to deleting 18 a time (returned in the results). For some reason I couldn't see this in the Azure Portal.
Anyway, pretty handy even if limited to a certain amount of deletes per execution. Executing the sp is also throttled so you can't just mash the keyboard. I think I would just delete and recreate the Collection unless I had a manageable number of documents to delete (thinking <500).
Props to Mimi Gentz #Microsoft for sharing the script in the link above.
HTH
I want something like DELETE * FROM c WHERE c.DocumentType = 'EULA'
but, apparently, it doesn't work.
Deleting documents this way is not supported. You would need to first select the documents using a SELECT query and then delete them separately. If you want, you can write the code for fetching & deleting in a stored procedure and then execute that stored procedure.
I wrote a script to list all the documents and delete all the documents, it can be modified to delete the selected documents as well.
var docdb = require("documentdb");
var async = require("async");
var config = {
host: "https://xxxx.documents.azure.com:443/",
auth: {
masterKey: "xxxx"
}
};
var client = new docdb.DocumentClient(config.host, config.auth);
var messagesLink = docdb.UriFactory.createDocumentCollectionUri("xxxx", "xxxx");
var listAll = function(callback) {
var spec = {
query: "SELECT * FROM c",
parameters: []
};
client.queryDocuments(messagesLink, spec).toArray((err, results) => {
callback(err, results);
});
};
var deleteAll = function() {
listAll((err, results) => {
if (err) {
console.log(err);
} else {
async.forEach(results, (message, next) => {
client.deleteDocument(message._self, err => {
if (err) {
console.log(err);
next(err);
} else {
next();
}
});
});
}
});
};
var task = process.argv[2];
switch (task) {
case "listAll":
listAll((err, results) => {
if (err) {
console.error(err);
} else {
console.log(results);
}
});
break;
case "deleteAll":
deleteAll();
break;
default:
console.log("Commands:");
console.log("listAll deleteAll");
break;
}
And if you want to do it in C#/Dotnet Core, this project may help: https://github.com/lokijota/CosmosDbDeleteDocumentsByQuery. It's a simple Visual Studio project where you specify a SELECT query, and all the matches will be a) backed up to file; b) deleted, based on a set of flags.
create stored procedure in collection and execute it by passing select query with condition to delete. The major reason to use this stored proc is because of continuation token which will reduce RUs to huge extent and will cost less.
##### Here is the python script which can be used to delete data from Partitioned Cosmos Collection #### This will delete documents Id by Id based on the result set data.
Identify the data that needs to be deleted before below step
res_list = "select id from id_del"
res_id = [{id:x["id"]}
for x in sqlContext.sql(res_list).rdd.collect()]
config = {
"Endpoint" : "Use EndPoint"
"Masterkey" : "UseKey",
"WritingBatchSize" : "5000",
'DOCUMENTDB_DATABASE': 'Database',
'DOCUMENTDB_COLLECTION': 'collection-core'
};
for row in res_id:
# Initialize the Python DocumentDB client
client = document_client.DocumentClient(config['Endpoint'], {'masterKey': config['Masterkey']})
# use a SQL based query to get documents
## Looping thru partition to delete
query = { 'query': "SELECT c.id FROM c where c.id = "+ "'" +row[id]+"'" }
print(query)
options = {}
options['enableCrossPartitionQuery'] = True
options['maxItemCount'] = 1000
result_iterable = client.QueryDocuments('dbs/Database/colls/collection-core', query, options)
results = list(result_iterable)
print('DOCS TO BE DELETED : ' + str(len(results)))
if len(results) > 0 :
for i in range(0,len(results)):
# print(results[i]['id'])
docID = results[i]['id']
print("docID :" + docID)
options = {}
options['enableCrossPartitionQuery'] = True
options['maxItemCount'] = 1000
options['partitionKey'] = docID
client.DeleteDocument('dbs/Database/colls/collection-core/docs/'+docID,options=options)
print ('deleted Partition:' + docID)

PHP + recursive menu tree and one path

Greeting,
am working on a recursive tree menu, but am getting stuck on somthing.
Recursive code
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$get_array[$don_1['id']] = array("id" => $don_1['id'], "id_parent" => $don_1['id_parent'], "title" => $don_1['title']);
}
function tree_2($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."id : ".$value['id']." | id_parent : ".$value['id_parent']." | title : ".$value['title']."<br/>";
$currLevel++;
$children = tree_2($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree_2($get_array,$get_parent);
My table
== Table structure for table category
|------
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
|title|varchar(20)|No|
|id_parent|int(11)|Yes|NULL
|path|varchar(11)|No|
== Dumping data for table category
|1|ELECTRONICS|0|0
|2|TELEVISIONS|1|1
|3|TUBE|2|2
|4|LCD|2|2
|5|PLASMA|2|2
|6|PORTABLE ELECTRONICS|1|1
|7|MP3 PLAYERS|6|2
|8|FLASH|7|2
|9|CD PLAYERS|6|2
|10|2 WAY RADIOS|6|3
|11|PLANS|0|0
|12|SPITFIRE|11|1
|13|MP3 PLAYERS|8|2
This code work's very well,
however am stuck for when i want to get only one branch. Like
FLASH
CD PLAYERS
WAY RADIO.
In fact, i'd like to do like into forums, where we can get the path back to the forum root. Like
FLASH > CD PLAYERS > WAY RADIO.
If any idea !
after some try, I finally found a solution to get from a php recursive tree only one branch. am searing the code, for those who are looking for this kind of script.
Sample source come from this blog : Managing Hierarchical Data in MySQL
The table source :
===Database model3
== Table structure for table category
|------
|Column|Type|Null|Default
|------
|//**id**//|int(11)|No|
|title|varchar(20)|No|
|id_parent|int(11)|Yes|NULL
|path|varchar(11)|No|
== Dumping data for table category
|1|ELECTRONICS|0|0
|2|TELEVISIONS|1|1/2
|3|TUBE|2|1/2/3
|4|LCD|2|1/2/4
|5|PLASMA|2|1/2/5
|6|PORTABLE ELECTRONICS|1|1/6
|7|MP3 PLAYERS|6|1/6/7
|8|FLASH|7|1/6/7/8
|9|CD PLAYERS|6|1/6/9
|10|2 WAY RADIOS|6|1/6/10
|11|PLANS|0|0
|12|SPITFIRE|11|11/12
|13|LOW QUALITY|8|1/6/7/8/13
Before the code, some explanation :
With php recursive tree, we are able to get from an array a tree with its branch, like this :
ELECTRONICS
TELEVISIONS
TUBE
LCD
PLASMA
PORTABLE ELECTRONICS
MP3 PLAYERS
FLASH
LOW QUALITY
CD PLAYERS
2 WAY RADIOS
PLANS
SPITFIRE
The very simple code that I use for the sample over :
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$get_array[$don_1['id']] = array("id" => $don_1['id'], "id_parent" => $don_1['id_parent'], "title" => $don_1['title']);
}
mysqli_free_result($rep_1);
function tree($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."title : ".$value['title']."<br/>";
$currLevel++;
tree($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree($get_array,$get_parent);
The The Adjacency List Model is great for simple mysql query, however, after several days in google search, I found that it was a real head hash to Retrieving a Single Path as we must know the final level, for static menu its fine, but for other purpose like forum parent or pages parent I found it wasn't the best way to process for Retrieving a Single Path.
I documented about the The Nested Set Model, on paper, it's great. But I found it was a bit a mess when INSERT / UPDATE and DELETE are requested.
I finally did some test with the path enumeration : Hierarchical data in MySQL (and other RDBMS) and found a solution for Retrieving a Single Path, like this :
ELECTRONICS
PORTABLE ELECTRONICS
FLASH
LOW QUALITY
The very simple code that am using :
$get_parent = "0";
$rep_1 = mysqli_query($connexion,' SELECT * FROM category WHERE id=7');
$get_array = array();
while($don_1 = mysqli_fetch_array($rep_1))
{
$explod_array = explode("/",$don_1['path'],9999);
foreach ($explod_array as $key=>$value)
{
$rep_2 = mysqli_query($connexion,' SELECT * FROM category WHERE id="'.$value.'"');
while($don_2 = mysqli_fetch_array($rep_2))
{
$get_array[$don_2['id']] = array("id" => $don_2['id'], "id_parent" => $don_2['id_parent'], "title" => $don_2['title']);
}
mysqli_free_result($rep_2);
}
}
mysqli_free_result($rep_1);
function tree_1($array,$parent,$currLevel=0)
{
foreach($array as $key => $value)
{
if($value['id_parent'] == $parent)
{
echo "".str_repeat("-", $currLevel)."id : ".$value['id']." | id_parent : ".$value['id_parent']." | title : ".$value['title']."<br/>";
$currLevel++;
tree_1($array,$key,$currLevel);
$currLevel--;
}
}
}
echo tree_1($get_array,$get_parent);
This way, I keep the same php recursive tree menu code. I do not charge my mysql table with a big query.
The badest point, is that I will have to code a bit more for the INSERT / UPDATE AND DELETE query, but I already worked on it, and it's doable with a little code.
I hope it will help.

LINQKit predicate for code first

I have read this (http://www.albahari.com/nutshell/predicatebuilder.aspx)
Here is my code :
var predicateOuter = PredicateBuilder.True<T_Users>();
predicateOuter.And(d => d.code== 357);
var count=tService.GetCount(predicateOuter.Expand());
my service in code first:
public int GetCountSearch(Expression<Func<T, bool>> exp)
{
return _entities.Count(exp);
}
all record in T_Users: 6548
all record where code==357 : 26
But it always returns all records. but why ?
You need to use the results of Add:
// Assign result here to predicateOuter -
predicateOuter = predicateOuter.And(d => d.code== 357);
// This should now function properly
var count = tService.GetCount(predicateOuter.Expand());
Add doesn't modify the predicate, but rather returns a new one with the additional criteria.

Resources