Firestore sort collection by field in reference - google-cloud-firestore

I have a collection containing posts which are documents containing a timestamp and other data, we could call it global post object.
Whenever an user posts something, a document with the same id as the post id is added to the user's private post collection (under users/userUuid/posts/postId), let's call it private post object.
Each private post object contains a reference to the global post object (stored as a reference object in the document).
This is the structure:
posts: [
"post1" : {
timestamp: 12000000000
data: "abc"
}
"post2" = {
timestamp: 12000000000
data: "abc"
}
]
users: [
"user1" : {
posts: [
"post1": {
ref: reference to post1
}
]
}
]
I have a screen in which I'm querying all the objects under the user's private post collection. Is it possible to sort those based on the timestamp value of the document they reference?

Is it possible to sort those based on the timestamp value of the document they reference?
It seems you are trying to join the two data from two collection in Cloud/Firebase Firestore which is faster if there's a join queries but Firestore has no join queries. There are still ways but they do it differently, most cases they perform multiple read operations, store in array variable and sort it or using collection group query. It still depend on your codes and how you will handle the data, there are some questions that already explain it in multiple questions in Stack Overflow(ex. 1, 2).
I would suggest that it will be easier if you change your Firestore data structure. For example, add field(ex. author) that has a value of userid(ex. users1) to the posts collection so every post has an owner. Use where clause for author field and orderBy clause for timestamp field to sort it desc or asc:
structure:
posts: [
"post1" : {
timestamp: 12000000000
data: "abc"
author: "user1"
}
"post2" = {
timestamp: 12000000000
data: "abc"
author: "user1"
}
]

Related

Filter mongodb data via API requests

I am trying to figure out how to filter data in mongodb Atlas via API requests.
My json structure:
{
_id: ObjectId,
level: number,
names: Array
}
I learned how to filter by _id. I pass the id via url, and fetch data with filter parameter using $oid in the body.
But the question is: how to filter data by level and get the list of data assign to certain level? I don't know what to use instead of $oid 🤷🏻‍♂️ If I just change the id/_id to level I don't receive anything.
const {
query: { id },
method,
} = req
....
const readData = await fetch(`${baseUrl}/find`, {
...fetchOptions,
body: JSON.stringify({
...fetchBody,
filter: { _id: { $oid: id } },
}),
})
The difference here has to do with data types. Your current query, which works when filtering on _id is:
{ _id: { $oid: id } }
The $oid component of this query is related to the fact that you are using the (default) ObjectId type for that field. $oid itself is the extended JSON representation of that data type. Other languages and interfaces have different ways of representing or constructing ObjectIds.
You have a different data type for your level field - a number. So attempting to do a similar query on that field such as:
{ level: { $oid: <value>} }
Will either result in the query failing (if the ObjectId is unable to be created from <value> due to its specific structure) or in no documents being returned (since no documents have ObjectIds for this field).
Removing the wrapping $oid should solve the problem:
{ level: <value> }

Storing enum to MongoDb (for managing tag names)

If we have a collection of books, we can assign tags of authors into an array as follows:
Books collection
{
...,
"authors" : ["John Michaels", "Bill Williams"]
}
This can cause problems if an author's name changes.
Instead, I was thinking of assigning an integer value to each author and creating a 'tags' collection:
Tags collection
{
“tags” : [
{“John Michaels” : 0},
{“Jane Collins” : 1},
{“Bill Williams” : 2}
]
}
Here is my books collection, here we specify that ‘John Michaels’ and ‘Bill Williams’ are the authors:
{
…,
“authors” : [0, 2]
}
If I ever needed to change the author’s name ‘Bill Williams’ to ‘Bill H. Williams’, there would be no problem because the value stored in the books collection remains unchanged.
My Question is if MongoDB has something like enums that will automatically increment the integral value or if there is something else built into MongoDB to help with this type of situation.
Thank you
This is typical use case of referencing other collections. So, you should have 2 collections:
Authors collection:
{
_id: ObjectId,
name: String,
... // Other fields
}
Books collection:
{
_id: ObjectId,
authors: [ ObjectId ], // References to documents from Author collection
... // Other fields
}
So, in authors property of the Books collection, you store _id values of all the authors. Then when you fetch book document, you can easily fetch up-to-date authors data from Authors collection.

How can I query for a subdocument full of objects in Mongo?

So I have a document with an unknown number of objects in it, each with 2 properties. It's a collection of friend lists, and I'm trying to confirm if someone has a friend with a certain username before I allow a user to send a request. I'm keeping the list of friends in a subdocument, like this:
>>all the _id and other properties<<, "ownerFriends":[{"friendId":"an id goes here", "friendUsername": "username"}, {"friendId":"another id", "friendUsername":"username2"}]
I'm trying to do a query that will return username2 if given that as input, but I don't know how to do that with dot notation because I think you need to know the specific property to look for, and these are heterodox amounts of friend objects in the ownerFriends property.
If you want to select the ownerFriend object that has username as the friendUserName you can use the following selector (assuming your collection is called Friends):
Friends.find({
"ownerFriends.friendUsername": "username2"
}, {
fields: { "ownerFriends.$": 1}
});
You can find a detailed explanation of how to query an array of objects based on a property here:
http://www.curtismlarson.com/blog/2015/08/08/meteor-mongodb-array-property-selector/
In summary you have an object that contains keys, one of whose values is an array of objects. You can perform queries on the arrays using $elemMatch In your case:
MyCollection.find({ ownerFriends: { $elemMatch: { friendUsername: searchString }}});
Although I think you'll need to also query on the current user's _id. Not knowing the details of your collection, I can only speculate with:
MyCollection.find({ userId: Meteor.userId(), ownerFriends: { $elemMatch: { friendUsername: searchString }}});

Meteor: How do you populate a field from one collection into another collection with _id field?

In mongo I have a document that stores pending userId's in a collaborators object array and looks like this:
researchThread {
_id: 4374583575745756
pending: {
collaborators: [
{
userId: '13745845754745753'
},
{
userId: '23755845854745731'
},
{
userId: '33755845653741736'
}]
}
}
The userId is the _id field for the user from the users collection. Each user also has a name and an email field.
How can I populate the name and email fields from the user collection, into this document for each user in the researchThread.pending.collaborators object array? And also, will the populated data be reactive when used in the templates?
Loop through each collaborator, find the relevant user document by searching the users collection for the id, and update the researchThread document with that information.
The data will be reactive if the researchThread collection is a Meteor.Collection which you're drawing from in your templates.
However, why do you want to copy the user data? Why not just have Meteor query the users collection based on the researchThread userId when you need the data in the template?

Document References query example

If I choose to use Document References with a structure of Materialized Paths instead of the simple Embedded Documents how can I display the same results?
For example if I had Embedded docs I simply :
db.col.find({'user' : 'foo'})
and return:
{'user' : 'foo',
'posts' : [ {},
{},
{}
]
}
Which command should I use to display posts as an embedded array of that user? Or this can only happen client-side?
If it's document references,
users collection will contain:
{
_id : "foo",
// users details
}
and posts collection:
{
_id: "postid",
author: "foo"
// other fields
}
In this case,
1) First make query to get the user id from users collection.
2) Then send the user id to the posts collection to get all posts
var user = db.users.find({_id : "foo"});
// this is used to get user details or validate user and only after validation if you need to fetch the posts
var posts = db.posts.find({author: user._id });
As the documents are referenced, there will be a roundtrip to the server which is obvious.
I am not sure how you have used materialized path for this scenario, let me know the data structure of it and i would be able to mention the query based on that.