My use case is that I have an <iron-form> with a single <paper-textarea> field that accepts a string list of email addresses which I parse into an array, then I want to:
Store the individual email addresses in my Firebase (for indexing and lookup purposes),
at multiple locations (per data fan out technique),
with a single write operation (because I don't want to make 100 API calls if the list is that long) and
without overwriting any existing data.
Specifically, I want to start with State A, as follows:
State A
my-app
|
- emails
| |
| - email1#example,com
| |- old: "data"
| - email2#example,com
| |- old: "data"
- users
|
- c0djhbQi6vc4YMB-fDgJ
And achieve State B as follows:
State B
my-app
|
- emails
| |
| - email1#example,com
| |- old: "data"
| - email2#example,com
| |- old: "data"
| - email3#example,com
| |- new: "data"
| - email4#example,com
| |- new: "data"
- users
|
- c0djhbQi6vc4YMB-fDgJ
|
- emails
|
- email3#example,com
|- new: "data"
- email4#example,com
|- new: "data"
Notice: The {old: "data"} is not overwritten.
Background
I seek to extend this SO question and answer.
There, we inserted a single node in a new location with three options:
using firebase-query
JS SDK
using firebase-document
Now, I need to do the same type of insertion (without deletion or replacing old data) for multiple nodes (with a user defined, not autogenerated, key; i.e., keys are specific email addresses). I also need to use the data fan out technique to update multiple paths with a single write operation.
Similar to what's shown here.
https://firebase.google.com/docs/database/web/read-and-write#update_specific_fields
function writeNewPost(uid, username, picture, title, body) {
// A post entry.
var postData = {
author: username,
uid: uid,
body: body,
title: title,
starCount: 0,
authorPic: picture
};
// Get a key for a new Post.
var newPostKey = firebase.database().ref().child('posts').push().key;
// * * * * * * * * * * * * * * * *
// THE ABOVE LINE NEEDS TO CHANGE TO SUPPORT USER-GENERATED KEYS SUCH AS EMAIL ADDRESSES
// * * * * * * * * * * * * * * * *
// Write the new post's data simultaneously in the posts list and the user's post list.
var updates = {};
updates['/posts/' + newPostKey] = postData;
updates['/user-posts/' + uid + '/' + newPostKey] = postData;
return firebase.database().ref().update(updates);
}
Also note, one of the comments mentions:
There's no reason newPostKey above couldn't be an email address...
The challenge is that I need to write multiple keys to multiple locations simultaneously in a single call.
The Firebase Realtime Database supports arbitrarily complex atomic deep updates (blog post). It works like so:
You can update any arbitrarily deep path with a single .update() call
The full path on the key side of your update map will be replaced, so you must address subkeys directly if you don't want to blow away the parent
Paths are relative to your current ref
So let's take your example:
var update = {};
update['emails/email3#example,com'] = {new: 'data'};
update['emails/email4#example,com'] = {new: 'data'};
update['users/c0djhbQi6vc4YMB-fDgJ/emails/email3#example,com'] = {new: 'data'};
update['users/c0djhbQi6vc4YMB-fDgJ/emails/email4#example,com'] = {new: 'data'};
firebase.database().ref().update(update);
This will update all of the locations simultaneously. To make it dynamic, simply use string interpolation when constructing the keys.
Related
I am trying to access function parameters within the 'case' statement in that function and displaying data/"filtered" based on the permission flag..Is it possible?
Usecase: TypeCast the value based on the columnType and check if the user has the permission to view the column based on which you display either the value or say something like "filtered"
Here is what I tried
function rls_columnCheck
.create-or-alter function rls_columnCheck(tableName:string, columnName: string, value:string, columnType:string, IsInGroupPII:bool, IsInGroupFinance:bool) {
let PIIColumns = rls_getTablePermissions(tableName, "PII");
let FinanceColumns = rls_getTablePermissions(tableName, "Finance");
let val= case(columnType=="bool", tobool(value),
columnType=="datetime", todatetime(value),
columnType=="int", toint(value),
value);
iif(columnName in (PIIColumns),
iif(columnName in (FinanceColumns),
iif(IsInGroupPII == true and IsInGroupFinance == true,
val,
"filtered"), // PII True, Fin True
iif(IsInGroupPII == true,
val,
"filtered") // PII True, Fin False
),
iif(columnName in (FinanceColumns),
iif(IsInGroupFinance == true,
val,
"filtered"), // PII False, Fin True
val // PII False, Fin False
)
);
}
Error:
Call to iff(): #then data type (int) must match the #else data type (string)
val in your function must have a single and well-defined data type, that is known at "compile" time of the query.
you can't have different cases, where in each it has a different type (bool, datetime, int, string - in your case statement) - hence the error.
if it makes sense in your use case, you can try to always have val typed as string.
This is not a good approach to use RLS because this will actually cause the engine to run a function for every column of every record. It has many downsides:
Performance of displaying the table’s contents (even if you have full permissions)
Queries on the table won’t benefit from the indexes Kusto stores (suppose you query PermissionTesting2 | where Col1 has “blablabla” - instead of checking the index for “blablabla”, the engine will have to scan all the data, because it has to apply a function for every single cell)
A better approach is to do something like this:
let UserCanSeePII = current_principal_is_member_of('aadgroup=group1#domain.com');
let UserCanSeeFinance = current_principal_is_member_of('aadgroup=group2#domain.com');
let ResultWithPII = YourTable | where UserCanSeePII and (not UserCanSeeFinance) | where ... | extend ...;
let ResultWithFinance = YourTable | where UserCanSeeFinance and (not UserCanSeePII) | where ... | extend ...;
let ResultWithPIIandFinance = YourTable | where UserCanSeeFinance and UserCanSeePII | where ... | extend ...;
let ResultWithoutPIIandFinance = YourTable | where (not UserCanSeePII) and (not UserCanSeeFinance) | where ... | extend ...;
union ResultWithPII, ResultWithFinance, ResultWithPIIandFinance, ResultWithoutPIIandFinance
KQL beginner here - I have some CEF logs hitting one of my servers and I need to get into the data to get some meaningful reports from it.
Take this log - not json, just a string
CEF:0|vendor1|vendorproduct|1.0|Event1|Event2|1|source_ip=0.0.0.0 rt=2020-04-28T04:17:05.475Z data1=example1 group=example2 endpoint=55555555 user=444444
I want to access each field and store as a var for further query use. What is the best way to achieve this so I can have results such as the below? Regex? String functions?
| extend vendorname = // = vendor1
| extend source_ip = // = 0.0.0.0
| extend endpoint = // = 55555555
// etc
OK, I figured this one out - see below for KQL to achieve what I was looking for:
Syslog
| where SyslogMessage has "vendor-name"
| extend logs = split(SyslogMessage, "|")
| extend vendor = logs[1]
| extend app = logs[2]
| extend version = logs[3]
| extend event = logs[4]
| extend msg = logs[5]
| parse SyslogMessage with * "source_ip=" source_ip "rt=" rt " id=" id " data1=" data1 " group=" group " endpoint=" endpoint "user=" user
| project vendor, app, version, event, msg, rt, data1, source_ip, id, group, endpoint, user
I have a situation where I need to calculate the distance between users. In this particular scenario I have:
Employer geolocation - One user one location.
Candidate geolocation - One location for each user but when the employer generates the list of candidates there are multiple candidates.
I'm currently successfully pushing geolocations down the wire and running a rudimentary distance formula on the client side to figure out the distance between the employer and each candidate on the fly and then showing/hiding the candidates, as per the request.
I've been told that I should be running the calculation on the server side and just pushing down a single number, ie. 10 representing 10km, for each candidate. Then running a filter on that number.
So far, I have only pushed collections and fields down the wire. Is it possible to run the formula below on the server side and just pass down one number and 'attach' it to a user?
The second question is, what would be best practice for Meteor?
I'm still learning to code, so apologies if this is a really obvious question.
Client Side
Path: List.js
specialisations = specialisations.filter(function(element){
let distance = Template.instance().distanceFromEmployerFilter.get();
let user = Meteor.users.findOne({_id: element.candidateUserId});
let candidateLat = user && user.profile && user.profile.address && user.profile.address.latitude;
let candidateLong = user && user.profile && user.profile.address && user.profile.address.longitude;
let company = CompanyDetails.findOne({employerUserId: Meteor.userId()});
let companyLat = company && company.latitude;
let companyLong = company && company.longitude;
var R = 6371; // Radius of the earth in km
var dLat = (companyLat-candidateLat) * (Math.PI/180);
var dLon = (companyLong-candidateLong) * (Math.PI/180);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos((candidateLat) * (Math.PI/180)) * Math.cos((companyLat) * (Math.PI/180)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distanceInKM = R * c; // Distance in km
if (distanceInKM <= distance) {
return true;
} else {
return false;
}
});
I'd do the filtering on the fetching of the candidates to display. Either as you publish/subscribe on your template or as you fetch in your helper:
Meteor.users.find({
"profile.address" : {
$near: {
$geometry: {
type: "Point" ,
coordinates: [ <Employerlongitude> , <Employerlatitude> ]
},
$maxDistance: <distance in meters>,
$minDistance: <distance in meters>
}}}).fetch();
If address is a 2d index. Specify coordinates in this order: “longitude, latitude.”
from Mongodb docs :
$near Specifies a point for which a geospatial query returns the
documents from nearest to farthest. The $near operator can specify
either a GeoJSON point or legacy coordinate point.
$minDistance & $maxDistance are optional
Just beginning with firebase... :-(
How do I set a property of an item?
This is my data structure:
myproject
|
- players
|
- -JPUAYuKUNeevXxaCMxM
|
- name: "John"
|
- skill
|
- mu: 25
|
- sigma: 8.333
- -JPUAYuRyJBH8sF93pNt
...
I can add a player with:
var ref = new Firebase(FIREBASE_URL + '/' + 'players');
ref.child(id).set(player);
The question is: how do I update only one property of an item (for example, 'skill') ?
I did try with:
var skill = {};
skill.mu = 27.0;
skill.sigma = 7.0;
ref.child(id).skill.update(skill);
I think I know what's going on here.
You expected ref.child(id) to have a property skill. However, you actually want the "skill" child; ref.child(id).child("skill").
I am writing generator plugin and I get document structure using method generator.getDocumentInfo(). It returns document object containing layer objects in tree structure. document object has property document.id and each layer has property layer.id.
Goal: I want to hide layer - I know only document id and layer id.
Problem: The only method to hide layer in generator plugin I found is evaluateJSXString() method. This is fine but I don't know how to access document by id and layer by id. According http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop-cc-javascript-ref.pdf document has no id property and layer also has no id property. All I found is that app.documents is an array of documents (but index is not id) and app.document[i].layers is an array of layers but it contains only top level layers and each top level layer contains child layers.
The only option I see is to write JSX script which will first traverse app.documents array to find doc with for example matching file name and then it will search for a layer in document.layers tree structure..
Is there any other options?
How generator provides document and layer IDs when using generator.getDocumentInfo()? Is some generatpr-internal notation?
I see this is an old question had the same question. I was able to come up with a solution using generator's method evaluateJSXString. You can execute extendscript with evaluateJSXString with in your generator plugin. There is no looping involved here. Just by layerID.
Note: layerID is a variable that holds the layer ID and it is concatenated to the string to be evaluated.
To show the layer:
var changeVisibilityString = " var ref = new ActionReference(); \
ref.putIdentifier(charIDToTypeID('Lyr '), " + layerID + " ); \
var desc = new ActionDescriptor(); \
desc.putReference(charIDToTypeID('null'), ref); \
desc.putBoolean(charIDToTypeID('MkVs'), false); \
executeAction( charIDToTypeID('Shw '), desc);"
generator.evaluateJSXString(changeVisibilityString);
To hide the layer:
var changeVisibilityString = " var ref = new ActionReference(); \
ref.putIdentifier(charIDToTypeID('Lyr '), " + layerID + " ); \
var desc = new ActionDescriptor(); \
desc.putReference(charIDToTypeID('null'), ref); \
desc.putBoolean(charIDToTypeID('MkVs'), false); \
executeAction( charIDToTypeID('Hd '), desc);"
generator.evaluateJSXString(changeVisibilityString);