Firebase equalto dynamic nested child - firebase

With a structure of
/archive: {
$userId: {
$archiveKey: {
foo: 1
},
...
},
...
}
Where $userId references a user id and $archiveKey is dynamic, created by .push().
Is it possible to query the archive ref and get all archiveObjects where foo = 1 ? Or do I need to fetch down the whole table, manually dig into $userId and extract the archiveObjects I'm looking for?

Firebase queries can nowadays query nested paths, but the paths cannot be dynamic.
So if you know the uid, you can query that user's archives with:
ref.child(authData.uid).orderByChild('foo').equalTo(1).on(...
If you don't know the uid, then you'll have to create a data structure that allows you to do the lookup:
archive_category_to_uids: {
foo: {
1: {
uid1: true,
uid2: true
}
}
}
A more common way is to separate the archives into their own top-level list and have both users and categories refer to that:
users: {
userId1: {
archiveKey1: true,
...
},
...
},
archives: {
archiveKey1: {
foo: 1,
uid: uid1
},
...
},
archiveCategories: {
foo: {
1: {
archiveKey1: true,
archiveKey2: true
}
}
}
Now you can get find the archives with:
ref.child('archiveCategories/foo/1').once('value', function(keys) {
keys.forEach(function(key) {
ref.child('archives').child(key.key()).once('value', function(snapshot) {
console.log(snapshot.val());
});
};
});
This process is called denormalization and is quite common in NoSQL databases. You're modeling the data for how your application needs to consume it. For more on this and other common patterns, I recommend reading this article on NoSQL data modeling.

Related

How to Publish joined Data from Array of IDs in Meteor

I just want to Publish the relational Data for a Publication to client, but the issue is my Relational Data field is array of ID's of a Different Collection, I tried Different Packages but all works with single Relational ID but not working with Array of relational ID's, let assume I have two Collection Companies and Meteor.users below is my Company Document Looks like
{
_id : "dYo4tqpZms9j8aG4C"
owner : "yjzakAgYWejmJcuHz"
name : "Labbaik Waters"
peoples : ["yjzakAgYWejmJcuHz", "yjzakAgYWejmJcuHz"],
createdAt: "2019-09-18T15:33:29.952+00:00"
}
here you can see peoples field contains the user ID's as Array, so How I publish this userId's as user Documents, as for example I tried the most popular meteor package named publishComposit, when I tried Loop in Children's find, I got undefined in children i.e below
publishComposite('compoundCompanies', {
find() {
// Find top ten highest scoring posts
return Companies.find({
owner: this.userId
}, {sort: {}});
},
children: [
{
find(company) {
let cursors = company.peoples.forEach(peopleId => {
console.log(peopleId)
return Meteor.users.find(
{ _id: peopleId },
{ fields: { profile: 1 } });
})
//here cursor undefined
console.log(cursors)
return cursors
}
}
]
});
and if I implement async loop in children's find I got error like below code
publishComposite('compoundCompanies', {
find() {
// Find top ten highest scoring posts
return Companies.find({
owner: this.userId
}, {sort: {}});
},
children: [
{
async find(company) {
let cursors = await company.peoples.forEach(peopleId => {
console.log(peopleId)
return Meteor.users.find(
{ _id: peopleId },
{ fields: { profile: 1 } });
})
//here cursor undefined
console.log(cursors)
return cursors
}
}
]
});
the error occured in above code is Exception in callback of async function: TypeError: this.cursor._getCollectionName is not a function
I don't know what I am exactly doing wrong here, or implementing package function not as intended any help will be greatly appropriated
EDIT: my desired result should be full user documents instead of ID no matter it mapped in same peoples array or as another fields I just want as below
{
_id: "dYo4tqpZms9j8aG4C",
owner: "yjzakAgYWejmJcuHz",
name: "Labbaik Waters",
peoples: [
{
profile: {firstName: "Abdul", lastName: "Hameed"},
_id: "yjzakAgYWejmJcuHz"
}
],
createdAt: "2019-09-18T15:33:29.952+00:00"
}
I ran into a similar problem couple of days ago. There are two problems with the provided code. First, using async; it's not needed and rather complicates things. Second, publishComposite relies on receiving one cursor not multiple within its children to work properly.
Below is a snippet of the code used to solve the problem I had, hopefully you can replicate it.
Meteor.publishComposite("table.conversations", function(table, ids, fields) {
if (!this.userId) {
return this.ready();
}
check(table, String);
check(ids, Array);
check(fields, Match.Optional(Object));
return {
find() {
return Conversation.find(
{
_id: {
$in: ids
}
},
{ fields }
);
},
children: [
{
find(conversation) {
// constructing one big cursor that entails all of the documents in one single go
// as publish composite cannot work with multiple cursors at once
return User.find(
{ _id: { $in: conversation.participants } },
{ fields: { profile: 1, roles: 1, emails: 1 } }
);
}
}
]
};
});

Meteor nested publications

I have two collections A and B in Meteor. For A I have a publication where I filter out a range of documents in A. Now I want to create a publications for B where I publish all documents in B that have a field B.length matching A.length.
I have not been able to find any example where this is shown but I feel it must be a standard use case. How can this be done in Meteor?
This is a common pattern for reywood:publish-composite
import { publishComposite } from 'meteor/reywood:publish-composite';
publishComposite('parentChild', {
const query = ... // your filter
find() {
return A.find(query, { sort: { score: -1 }, limit: 10 });
},
children: [
{
find(a) {
return B.find({length: a.length });
}
}
]
});
This is a quite different pattern than serverTransform as on the client you end up with two collections, A and B, as opposed to a synthetic single collection A that has some fields of of B. The latter is more like a SQL JOIN.
Use serverTransform
Meteor.publishTransformed('pub', function() {
const filter = {};
return A.find(filter)
.serverTransform({
'B': function(doc) {
return B.find({
length: doc.length
}); //this will feed directly into miniMongo as if it was a seperate publication
}
})
});

Structuring firebase database, dependency on two variables

I'm having some trouble structuring my database.
Currently I have users and groups structured as following:
-groups
-groupId
-groupname: "...."
-users
-userId: true
-users
-userId
-username: "..."
-groups
-groupId: true
Now I want to add variables that are dependent on both groups and user. I tried making another tree called groupUser containing both a groupId and userId, but it's hard to query on two conditions with Firebase. So what would be the best way to structure the database?
Thanks in advance!
I would structure your data like so:
{
groups: {
groupIdOne: {
groupname: "...",
users: {
userIdOne: true
}
}
},
users: {
userIdOne: {
username: "...",
groups: {
groupIdOne: true
}
}
},
groupUsers: {
groupIdOne: {
userIdOne: {
//data for group users
}
}
},
//and/or
userGroups: {
userIdOne: {
groupIdOne: {
//data for user groups, same as above
}
}
}
}
This way if you have a group id and a user id you just query that node directly by doing something like .child('groupUsers').child(groupId).child(userId)

Redux and Normalizr placing single entity into array

I'm using Redux and Normalizr.
I have a state which looks like:
{
entities: {
users: {
1: {...},
2: {...}
}
},
data: {
users: [1,2]
}
}
One of my API endpoints returns an object that includes a single user and some other data:
data = {
user: {id: 3, ...}
otherstuff: {...}
}
const user = new Schema('users');
normalize(data, {user: user})
This returns
{
entities: {
users: {
3: {id: 3, ...}
}
},
result: {
user: 3
}
}
However my data reducer expects to merge in a users array (of ids):
function data(state = {}, action) {
if (action.response && action.response.result) {
return deepmerge(state, action.response.result)
}
return state
}
Ideally I'd address this issue in the normalisation process rather than changing the reducer. Is there an easy way to either get normalizr to parse {user: {id: 3}} into {result: {users: [3]}}? It already changes the entities key to users.
If not, is there a clean and generic reducer-level solution to having this problem across a variety of entity type names?

In Collection.find, how to format .limit, .sort, fieldlist, and variable column names

In non Meteor Server-Side calls to mongodb it is possible make the following chained-option call to the database
collection.find( { myField: { $gte: myOffset } ).limit( myLimit ).sort( { mySortField : 1 } );
where myField, myOffset, myLimit and mySortField may be resolved from elsewhere at run-time.
This pattern is very useful to create such a run-time generated generic query.
Meteor seems to insist on the non-chained options pattern of
collection.find( { { myField: { $gte: myOffset } }, { limit: myLimit, sort: { mySortField : 1 }} );
and I am having problems 'building up' a working Find Query as required above from js objects as described
in previous questions 17362401 and 10959729
Would anyone like to help?
Edited to show usage of variable:
I do it this way. You send two hashes, where the first is the where clause, and all else are peer level keys.
var locations;
var myfield = 'gps';
search = {
sureties: {
$in: sureties
}
}
search[myfield] = {
$near: this.gps,
$maxDistance: kilometers
};
locations = Agents.find(search, {
fields: {
name: 1,
phone: 1
},
limit: limit,
sort: { field1 : 1 }
}).fetch();
The chained pattern is not possible in Meteor, neither server side nor on the client. But the params pattern is as universal, you should be able to create any query you need with those params.

Resources