I have a weird situation on my project. I have 2 entities: children and users. Users create children, so on children, I have a reference to their owners and on users I have a list of the children they have created.
When I add a new child to firebase, the user owner of that child has his list of children updated to add that new child.
The thing is: on the staging environment everything works as it should be. But on the production environment, the child is added to the children, but not to the list of children on users.
Both databases have the same rules.
I've got to this project when it was already partially developed, so I can't even find where specifically the user should be updated. It seems to me that when you update a child, the users entity is automagically updated (using breakpoints I could verify it actually happens on the same step). I'm kind of new to Firebase so I am really lost here.
Can anyone try to help me?
Here are the database rules:
{"rules": {
"children": {
"$uid": {
".validate": "newData.hasChildren(['first_name'])",
"date_of_birth": {
".validate": "newData.isString() && newData.val().matches(/^(19|20)\\d\\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/)"
},
"first_name": {
".validate": "newData.isString() && newData.val().length > 0"
},
"users": {
"$key2": {
".validate": "root.child('users').child($key2).val() != null && newData.isString() && newData.val() == 'parent'"
},
".validate": "newData.hasChildren()"
},
"$other": {
".validate": "false"
},
".read": "data.child('users').child(auth.uid).val() != null",
".write": "data.child('users').child(auth.uid).val() == 'parent' || newData.child('users').child(auth.uid).val() == 'parent'"
}
},
"users": {
".indexOn": [
"email"
],
"$uid": {
".validate": "newData.hasChildren()",
"email": {
".validate": "newData.isString() && newData.val() == auth.token.email"
},
"first_name": {
".validate": "newData.isString()"
},
"is_subscribed": {
".validate": "newData.isBoolean()"
},
"children": {
"$key5": {
".validate": "root.child('children').child($key5).val() != null && newData.isString() && newData.val() == 'parent'"
},
".validate": "newData.hasChildren()"
},
"$other": {
".validate": "false"
},
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
and the code that adds children to it.
var entityDatabase: DatabaseReference = Database.database().reference().child(“children”).child(did)
entityDatabase.keepSynced(true)
var firstName: String?
var users: [String: String] = [:]
var params: [String: Any] {
var strings: [String: String] = [:]
strings["first_name"] = firstName
let keysToRemove = strings.keys.filter { strings[$0] == "" }
for key in keysToRemove {
strings.removeValue(forKey: key)
}
var dict: [String: Any] = strings
dict["users"] = users
return dict
}
guard let user = Firebase.Auth.auth().currentUser else {
return
}
guard firstName?.isEmpty == false else {
return
}
users[user.uid] = "parent"
entityDatabase.updateChildValues(params) { [weak self] (error, _) in
self?.observe()
guard error == nil else {
return
}
switch self?.updatedImage {
case let image?:
self?.save(image: image, completion: completion)
case nil:
}
}
Related
I'm trying to write data to the realtime database by first logging in users always using firebase (google_auth).
But there is something wrong with my rules ... if I set write and read "true" they obviously go, the same if I just use "auth! = Null" but when I use these rules:
{
"rules": {
"users": {
"$uid": {
".read": "auth! = null && auth.uid == $ uid",
".write": "auth! = null && auth.uid == $ uid"
}
}
}
}
No longer goes. So I assume the problem is that auth.uid is not the same as uid. I am attaching the code used for authentication and data submission.
Login / registration:
Future <UserCredential> signInWithGoogle () async {
final GoogleSignInAccount? userGoogle = await GoogleSignIn (). signIn ();
final GoogleSignInAuthentication? Google user data =
await userGoogle? .authentication;
final credentialsGoogle = GoogleAuthProvider.credential (
accessToken: Google User data? .accessToken,
idToken: Google User data? .idToken,
);
return await FirebaseAuth.instance.signInWithCredential (Google credentials);
}
Data sending:
DatabaseReference ref = FirebaseDatabase.instance.ref ();
await ref.set ({
"users": {
"$uid": {
"name": "John",
}
}
});
uid is fetched like this:
checkLogin () {
FirebaseAuth.instance.authStateChanges (). Listen ((User? User) {
if (user! = null) {
username = user.displayName;
uid = user.uid;
}
});
}
i think it's because of wrong spacing.. edited the rules. Try this please
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
I wanted to know if it's possible that a newly authenticated user can insert only their information to the realtime database.
The flow would be something like:
User authenticates via my app where he has to input
Email
Username
...
The user is authenticated (via Firebase Authentication) and has a uid
User writes a new DB entry in the /users/{uid} field of his/her. However, they are not allowed to set the balances field below.
Email
Username
...
He/She should only be able to write and read his/her own /users/{uid} object.
I've tried these rules so far, but none of them lets me create a new /users/{uid}
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
"balances": {
".write": false
},
"userName": {
".write": "$uid === auth.uid",
},
"email": {
".write": "$uid === auth.uid",
},
"phoneNumber": {
".write": "$uid === auth.uid",
}
}
}
}
}
The code I want to insert:
// ...
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if let error = error {
self.error = error.localizedDescription
return
}
if let authResult = authResult {
// Auth successful
User.firebaseUser = authResult.user
print("Firebace UserId: \(authResult.user.uid)")
let ref: DatabaseReference! = Database.database().reference()
ref.child("users").child(authResult.user.uid).setValue([
"userName": self.userName,
"email": self.email
])
// ...
}
}
My Swift error message:
2022-05-26 16:55:55.043864+0200 Project[15240:1265346] 9.0.0 - [FirebaseDatabase][I-RDB038012] setValue: or removeValue: at /users/EZ2bNl3rMANtLtn1G9Wfqi0Ivjl1 failed: permission_denied
Thank you in advance!
I have added a function to search user on firebase.
Here is the code:
func searchUser(search: String, includeCurrentUser: Bool = true, completion: #escaping ([User]) -> (), withCancel cancel: ((Error) -> ())?) {
Database.database().reference().child("users").queryOrdered(byChild: "username").queryStarting(atValue: search , childKey: "username").queryEnding(atValue: search + "\u{f8ff}", childKey: "username").observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionaries = snapshot.value as? [String: Any] else {
completion([])
return
}
var users = [User]()
dictionaries.forEach({ (key, value) in
if !includeCurrentUser, key == Auth.auth().currentUser?.uid {
completion([])
return
}
guard let userDictionary = value as? [String: Any] else { return }
let user = User(uid: key, dictionary: userDictionary)
users.append(user)
})
users.sort(by: { (user1, user2) -> Bool in
return user1.username.compare(user2.username) == .orderedAscending
})
completion(users)
}){ (err) in
print("Failed to fetch all users from database:", (err))
cancel?(err)
}
}
When searchUser is called, my Xcode console states:
[Firebase/Database][I-RDB034028] Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding ".indexOn": "username" at /users to your security rules for better performance
Here are my firebase rules:
{
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
".read": true,
".write": true,
"posts" : {
".indexOn": "creationDate"
},
"purchaser" : {
"$postID" : {
".indexOn": ".value"
}
},
"users" : {
"$uid": {
".indexOn" : "username"
}
}
}
}
And here is my database structure:
So the searchUser function works, but the xcode console continue to asks for .indexOn for username.
Why is going wrong here?
Indexes needs to be defined on the location where you use them in your query. So to define an index on /users with the username property of each child node:
"users" : {
".indexOn" : "username"
}
This is a question based on MongoDb - remove all fields that are null. The referred post only gives solution that removes null fields at the top level. However, how can I remove the null fields that embedded?
Please note that I have no idea of the possible names of the null fields and their depth, so I think we have to iterate over each field of each document.
This is an example:
{
"id": 14770467,
"f1": "a",
"f2": null,
"f3": [
{
"id": 76946819,
"f4": null
}
]
}
I'm expecting something like this:
{
"id": 14770467,
"f1": "a",
"f3": [
{
"id": 76946819
}
]
}
Thanks.
try this
const remove = (data) => {
for (let key in data) {
const val = data[key];
if (val == null) {
delete data[key];
} else if (Array.isArray(val)) {
val.forEach((v) => {
remove(v);
});
}
}
return data;
}
db.getCollection('Collection').find({}).forEach((data) => {
data = remove(data);
db.getCollection('OtherCollection').insert(data);
//db.getCollection('Collection').save(data); // update same record
print(data);
})
Above was not working for me. But was inspiration to seek for more.
This worked (with MongoDB shell version v4.0.5):
const remove= (obj) => {
Object.keys(obj).forEach(key => {
if (obj[key] && typeof obj[key] === 'object') removeEmpty(obj[key]);
else if (obj[key] == null) delete obj[key];
});
};
db.getCollection('Collection').find({}).forEach((data) => {
remove(data);
db.getCollection('OtherCollection').insert(data);
})
I would like to create a MongoDB/mapReduce data source into icCube (http://www.iccube.com/support/documentation/user_guide/schemas_cubes/ds_mongodb.php#mapReduce), The below script works fine from Mongo shell, how it should be formatted to be accepted by icCube, when I paste the same code into icCube datasource builder, I get this error message:
MongoDB : invalid JSON (table:Test.mapReduce) : [var location_map = function() { if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) { emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0}); } } var country_map = function() { if (this.companyId = "1234") { emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId }); } } var r = function(key, values) { var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0}; values.forEach(function(value) { if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; } if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; } if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; } if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; } }); return result; } db.runCommand( { mapReduce: Location, map: location_map, reduce: r, out: { replace: LocationsCountries }, query: {companyId : "1234"} } ) db.runCommand( { mapReduce: Countries, map: country_map, reduce: r, out: { reduce: LocationsCountries }, query: {companyId : "1234" } } )] ^
Mongo mapReduce script:
var location_map = function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}
var country_map = function() {
if (this.companyId = "1234") {
emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId });
}
}
var r = function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}
db.runCommand(
{
mapReduce: "Location",
map: location_map,
reduce: r,
out: { replace: "LocationsCountries" },
query: {companyId : "1234"}
}
)
db.runCommand(
{
mapReduce: "Countries",
map: country_map,
reduce: r,
out: { reduce: "LocationsCountries" },
query: {companyId : "1234" }
}
)
Thanks,
Balint
You'll have to define your functions in strings ( and Javascript escape if required strings within these strings ). This is described here in the icCube documentation ( www ). Here is an example combining single quoted strings with double-quoted strings.
{
"mapReduce": "Location",
"map": 'function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}',
"reduce": 'function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}',
"out": {
"replace": "LocationsCountries"
},
"query": {
"companyId" : "1234"
}
}