ref.child('users').orderByChild("mobileNumber").equalTo(phoneNumber.number).once("value"), forEach() forEach().
, /users, mobileNumber phoneNumber.number , , , mobileNumber mobileNumberWithCC, forEach(). , , X Y , Z , X*Y*Z . , , , 60 .
, /phoneNumbers. /phoneNumbers n########### c########### "" , .
:
"phoneNumbers": {
"n1234567890": { // without CC, be warned of overlap
"userId1": true,
"userId3": true
},
"c011234567890": { // with CC for US
"userId1": true
},
"c611234567890": { // with CC for AU
"userId3": true
},
...
}
:
n########### c###########?
, Firebase . , n/c , .
n###########, c###########?
n, 11- 10- , . , n c , .
, /phoneNumbers "" ?
, Firebase ( ). , /phoneNumbers/n1234567890, . 1, - 2; 1 3. , , / , .
Cloud-, . .
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.handleNewUser = functions.database.ref('/users/{userId}')
.onWrite(event => {
var deltaSnapshot = event.data,
userId = event.params.userId,
tasks = [];
if (!deltaSnapshot.exists()) {
var previousData = deltaSnapshot.previous.val();
if (previousData.number) {
tasks.push(removeUserFromPhoneNumber(userId, previousData.number, false));
}
if (previousData.numberWithCC) {
tasks.push(removeUserFromPhoneNumber(userId, previousData.numberWithCC, true));
}
return Promise.all(tasks).then(() => {
console.log('User "' + userId + '" deleted successfully.');
});
}
var currentData = deltaSnapshot.val();
if (deltaSnapshot.previous.exists()) {
var previousData = deltaSnapshot.previous.val();
if (currentData.number != previousData.number) {
tasks.push(removeUserFromPhoneNumber(userId, previousData.number, false));
tasks.push(addUserToPhoneNumber(userId, currentData.number, false));
}
if (currentData.numberWithCC != previousData.numberWithCC) {
tasks.push(removeUserFromPhoneNumber(userId, previousData.numberWithCC, true));
tasks.push(addUserToPhoneNumber(userId, currentData.numberWithCC, true));
}
return Promise.all(tasks).then(() => {
console.log('User "' + userId + '" updated successfully.');
});
}
tasks.push(addUserToPhoneNumber(userId, currentData.number, false));
tasks.push(addUserToPhoneNumber(userId, currentData.numberWithCC, true));
return Promise.all(tasks).then(() => {
console.log('User "' + userId + '" created successfully.');
});
);
function lookupUsersByPhoneNumber(number, withCountryCode) {
if (!number) return Promise.reject(new TypeError('number cannot be falsy.');
return lookupIdsByIndex('phoneNumbers', (withCountryCode ? 'c' : 'n') + number);
}
function addUserToPhoneNumber(userId, number, withCountryCode) {
if (!number) return Promise.reject(new TypeError('number cannot be falsy.');
return addIdToIndex(userId, 'phoneNumbers', (withCountryCode ? 'c' : 'n') + number)
}
function removeUserFromPhoneNumber(userId, number, withCountryCode) {
if (!number) return Promise.reject(new TypeError('number cannot be falsy.');
return removeIdFromIndex(userId, 'phoneNumbers', (withCountryCode ? 'c' : 'n') + number)
}
function lookupIdsByIndex(indexName, keyName) {
if (!indexName) return Promise.reject(new TypeError('indexName cannot be falsy.');
if (!keyName) return Promise.reject(new TypeError('keyName cannot be falsy.');
return admin.database().ref(indexName).child(keyName).once("value")
.then(snapshot => {
if (!snapshot.exists()) return [];
var idsObject = snapshot.val();
if (idsObject == null) return [];
return Object.keys(idsObject);
});
}
function addIdToIndex(id, indexName, keyName) {
if (!id) return Promise.reject(new TypeError('id cannot be falsy.');
if (!indexName) return Promise.reject(new TypeError('indexName cannot be falsy.');
if (!keyName) return Promise.reject(new TypeError('keyName cannot be falsy.');
return admin.database().ref(indexName).child(keyName)
.transaction(function(idsObject) {
idsObject = idsObject || {};
if (idsObject.hasOwnProperty(id)) return;
idsObject[id] = true;
return idsObject;
});
}
function removeIdFromIndex(id, indexName, keyName) {
if (!id) return Promise.reject(new TypeError('id cannot be falsy.');
if (!indexName) return Promise.reject(new TypeError('indexName cannot be falsy.');
if (!keyName) return Promise.reject(new TypeError('keyName cannot be falsy.');
return admin.database().ref(indexName).child(keyName)
.transaction(function(idsObject) {
if (idsObject === null) return;
if (!idsObject.hasOwnProperty(id)) return;
delete idsObject[id];
if (Object.keys(idsObject).length === 0) return null;
return idsObject;
});
}
handleNewUser . Firebase ( FB ). ( ).
, :
contactsData.forEach((contact) => {
contact.phoneNumbers.forEach((phoneNumber) => {
var tasks = [];
tasks.push(lookupUsersByPhoneNumber(phoneNumber.number, false));
tasks.push(lookupUsersByPhoneNumber(phoneNumber.number, true));
Promise.all(tasks).then(taskResults => {
var i = 0;
var userIds = taskResults.reduce((arr, results) => {
for (i=0;i<results.length;i++) {
if (results[i] !== null && ~arr.indexOf(results[i])) {
arr.push(results[i]);
}
}
return arr;
}, []);
return userIds.map(uid => ({
name: contact.name,
phone: phoneNumber.number,
id: uid
}));
}).then(currentContacts => {
currentContacts.forEach(contact => {
contactsObj[contact.id] = contact
});
});
});
});
/
/phoneNumbers . , .
:
admin.initializeApp(functions.config().firebase);
:
admin.initializeApp(Object.assign({}, functions.config().firebase, {
databaseAuthVariableOverride: {
uid: "cloudfunc-service-worker" // change as desired
}
});
Firebase :
"rules": {
"phoneNumbers": {
".read": "'cloudfunc-service-worker' === auth.uid",
".write": "'cloudfunc-service-worker' === auth.uid"
}
}