Я немного поиграл с этим. Поскольку вы используете динамические ключи, не существует эффективного способа использования orderByChild()
для получения желаемого результата без необходимости загружать весь узел «ContactPhoneNumbers» на клиентское устройство, а затем выполнять сортировку и фильтрацию. Альтернативой является ведение индекса контактов.
orderByChild ()
Это полезно только для небольших наборов данных. Не надейтесь на это, когда «ContactPhoneNumbers» становится больше, так как это увеличит ваши расходы. Каждый раз, когда это вызывается, ваша база данных контактов будет полностью загружена.
String phoneSearchText = "+90 530 408 3456";
Query q = mUserDatabase.orderByChild(phoneSearchText).startAt("");
Индексация
Когда ваши данные основаны на динамических ключах, вы реализуете пользовательскую реализацию индекса, а не используете встроенную систему индексации RTDB (см. Индексация данных ).
Вы можете создавать и поддерживать индекс либо на стороне клиента (дешевле), либо на стороне сервера (проще в обслуживании).
Ради безопасности и удобства обслуживания я включу некоторый модифицированный код для реализации на стороне сервера, основанный на Облачные функции для Firebase .
Этот код сгенерирует следующий индекс, который может быть запрошен для номера телефона и содержит все различные варианты. Любые изменения, внесенные в дерево «ContactPhoneNumbers», будут автоматически отражаться в этом индексе. Вы должны защитить этот индекс от возможности его изменения клиентскими устройствами.
{
"ContactPhoneNumbersIndex": {
"+90 505 696 1234": {
"-LcaHYcsoGA-VT8yvgGf": "A",
"-LcaH_gtgarJwbY5-C08": "AA"
},
"+90 506 854 2345": {
"-LcaHYcsoGA-VT8yvgGf": "B",
"-LcaH_gtgarJwbY5-C08": "BB"
},
"+90 530 408 3456": {
"-LcaHYcsoGA-VT8yvgGf": "C",
"-LcaH_gtgarJwbY5-C08": "CAC"
},
"+90 535 966 4567": {
"-LcaHYcsoGA-VT8yvgGf": "D",
"-LcaH_gtgarJwbY5-C08": "AAA"
},
"+90 536 782 5678": {
"-LcaHYcsoGA-VT8yvgGf": "E",
"-LcaH_gtgarJwbY5-C08": "CAB"
},
"+90 546 934 67 89": {
"-LcaHYcsoGA-VT8yvgGf": "F",
"-LcaH_gtgarJwbY5-C08": "BB"
},
"+905304080001": {
"-LcaHYcsoGA-VT8yvgGf": "G",
"-LcaH_gtgarJwbY5-C08": "A"
},
"+905316910002": {
"-LcaHYcsoGA-VT8yvgGf": "H",
"-LcaH_gtgarJwbY5-C08": "BBB"
},
"+905359660003": {
"-LcaHYcsoGA-VT8yvgGf": "I",
"-LcaH_gtgarJwbY5-C08": "DDD"
},
"+905367820004": {
"-LcaHYcsoGA-VT8yvgGf": "J",
"-LcaH_gtgarJwbY5-C08": "EEE"
},
"+905425420005": {
"-LcaHYcsoGA-VT8yvgGf": "K",
"-LcaH_gtgarJwbY5-C08": "FFF"
},
"+905469340006": {
"-LcaHYcsoGA-VT8yvgGf": "L",
"-LcaH_gtgarJwbY5-C08": "L"
},
"05056960007": {
"-LcaHYcsoGA-VT8yvgGf": "M",
"-LcaH_gtgarJwbY5-C08": "M"
}
}
}
Поскольку вы используете push-идентификаторы в своей структуре базы данных, функции, написанные ниже, работают на уровне, содержащем push-идентификаторы, выполняя операции над целыми группами, а не над отдельными записями. Вы можете прочитать документацию по функциям здесь и посмотреть Firecasts по теме.
// Import and initialize Cloud Functions and Admin SDKs
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const ADMIN_DB_REF_PHONE_NUMBER_INDEX = admin.database().ref('/ContactPhoneNumbersIndex');
// Add entirely new contact groups to index
exports.contactGroups_created = functions.database.ref('/ContactPhoneNumbers/{groupPushId}')
.onCreate((snapshot, context) => {
const groupId = context.params.groupPushId;
// prepare atomic write
const pendingUpdates = {};
const contactsMap = snapshot.val();
for (const phone in contactsMap) {
if (contactsMap.hasOwnProperty(phone)) {
let name = contactsMap[phone];
// add data to update
pendingUpdates[`${phone}/${groupId}`] = name;
}
}
// commit the update to the index
return ADMIN_DB_REF_PHONE_NUMBER_INDEX.update(pendingUpdates);
});
// Remove deleted contact groups from index
exports.contactGroups_deleted = functions.database.ref('/ContactPhoneNumbers/{groupPushId}')
.onDelete((snapshot, context) => {
const groupId = context.params.groupPushId;
// prepare atomic write
const pendingUpdates = {};
const contactsMap = snapshot.val();
for (const phone in contactsMap) {
if (contactsMap.hasOwnProperty(phone)) {
let name = contactsMap[phone];
// add data to update
pendingUpdates[`${phone}/${groupId}`] = null; // null will delete data at given location
}
}
// commit the update to the index
return ADMIN_DB_REF_PHONE_NUMBER_INDEX.update(pendingUpdates);
});
// Handle contact changes
exports.contactGroups_changed = functions.database.ref('/ContactPhoneNumbers/{groupPushId}')
.onUpdate((change, context) => {
const groupId = context.params.groupPushId;
// prepare atomic write
const pendingUpdates = {};
// prepare changes map
const changeMap = {};
// add before state to changeMap
const beforeContactsMap = change.before.val();
for (const phone in beforeContactsMap) {
if (beforeContactsMap.hasOwnProperty(phone)) {
let name = beforeContactsMap[phone];
changeMap[phone] = { before: name };
}
}
// add after state to changeMap
const afterContactsMap = change.after.val();
for (const phone in afterContactsMap) {
if (afterContactsMap.hasOwnProperty(phone)) {
let name = afterContactsMap[phone];
if (changeMap[phone]) {
changeMap[phone].after = name;
} else {
changeMap[phone] = { after: name };
}
}
}
// look for changes and commit any differences
for (const phone in changeMap) {
if (changeMap.hasOwnProperty(phone)) {
let nameChange = changeMap[phone];
if (nameChange.before != nameChange.after) {
// changed
pendingUpdates[`${phone}/${groupId}`] = nameChange.after || null; // use updated value or fallback to null to delete
}
}
}
// commit the update to the index
return ADMIN_DB_REF_PHONE_NUMBER_INDEX.update(pendingUpdates);
});
Классы
С вашей структурой базы данных создание класса является сложной задачей. Один из способов - сделать шаг вверх по дереву узлов и посмотреть на контакты в группах, где вы можете обернуть класс вокруг внутренней карты ваших значений или списка объектов Contact.
Пример, который я привел ниже, поддерживает внутреннюю карту и взаимодействует с ней.
К сожалению, Firebase SDK не предоставляет метод «сериализации», похожий на пользовательские функции сравнения, поэтому вы не сможете использовать этот класс непосредственно с SetValue
и GetValue
.
Для загрузки в базу данных:
ContactGroup mContacts = ...
DatabaseReference groupRef = mContactGroupsReference.push();
mContacts.setId(groupRef.getKey()); // optional
groupRef.SetValue(mContacts.toMap());
Для скачивания из базы данных:
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
ContactGroup contacts = new ContactGroup(dataSnapshot);
// or
ContactGroup contacts = new ContactGroup(dataSnapshot.getKey(), dataSnapshot.getValue());
}
Этот класс неполный. Там достаточно, чтобы заставить соки течь, но есть много возможностей для улучшения.
public class ContactGroup {
private id = null;
private Map<String, String> contactMap = new HashMap<>(); // Phone => Name
public ContactGroup() {
}
public ContactGroup(String id, Map<String, Object> contacts) {
this.id = id;
if (contacts == null)
return;
for (Map.Entry<String, Object> entry : contacts.entrySet()) {
contactMap.put(entry.getKey(), entry.getValue().toString());
}
}
public ContactGroup(DataSnapshot snapshot) {
this(snapshot.getKey(), snapshot.getValue());
// do something else with snapshot? save the ref?
}
public void add(String name, String phone) {
contactMap.put(phone, name);
}
public String getNameForPhone(String phone) {
return contactMap.get(phone);
}
public String getPhoneForName(String name) {
for (Map.Entry<String, String> entry : contactMap.entrySet()) {
if (entry.getValue() == name)
return entry.getKey();
}
return null;
}
public getId() {
return id;
}
public setId() {
return id;
}
@Exclude
public static ContactGroup fromMap(Map<String, Object> valueMap) {
return new ContactGroup(null, valueMap);
}
@Exclude
public Map<String, Object> toMap() {
return new HashMap<String, Object>(contactMap);
}
}
Здесь есть что распаковать (вот почему ваш вопрос был опущен, будьте более конкретны в будущем). Удачи!