How do I query subcollections firestore ionic 4 - ionic-framework

Hello I want to make a query to a sub-collection in firestore I have the following structure
"groups": {
"g1":
{
"name": "Group 1",
"users": {
"u1": {
"id": "user1"
},
"u2": {
"id": "user2"
}
}
},
"g2":
{
"name": "Group 2",
"users": {
"u1": {
"id": "user1"
}
}
}
}
"users": {
"user1": {
"firstName": "Lorem",
"lastName": "Lorem"
},
"user2": {
"firstName": "Lorem2",
"lastName": "Lorem2"
}
}
and I want to make a query that looking for user1 brings me the groups that belong that user in the example would bring me g1 and g2 but if I look for user2 should I only bring g1 can you create a composite index between the group and the user? I am developing it in ionic 4 I don't know if the data is well structured
Thank you very much in what you can help me

You can use an array_contains operation on field users. All that requires is that you know the complete, exact element that the array should contains. If you know all of that, you can check with::
groupsRef.where("users", "array-contains", {
u1": {
"id": "user1"
}
})
If you only know "user1", you will need to have an array that contains only "user1". So for example:
"userids": ["user1", "user2"]
Then you can query with:
itiesRef.where("regions", "array-contains", "user1")

Related

MongoDB: $set specific fields for a document array elements only if not null

I have a collection with the following documents (for example):
{
"_id": {
"$oid": "61acefe999e03b9324czzzzz"
},
"matchId": {
"$oid": "61a392cc54e3752cc71zzzzz"
},
"logs": [
{
"actionType": "CREATE",
"data": {
"talent": {
"talentId": "qq",
"talentVersion": "2.10",
"firstName": "Joelle",
"lastName": "Doe",
"socialLinks": [
{
"type": "FACEBOOK",
"url": "https://www.facebook.com"
},
{
"type": "LINKEDIN",
"url": "https://www.linkedin.com"
}
],
"webResults": [
{
"type": "VIDEO",
"date": "2021-11-28T14:31:40.728Z",
"link": "http://placeimg.com/640/480",
"title": "Et necessitatibus",
"platform": "Repellendus"
}
]
},
"createdBy": "DEVELOPER"
}
},
{
"actionType": "UPDATE",
"data": {
"talent": {
"firstName": "Joelle new",
"webResults": [
{
"type": "VIDEO",
"date": "2021-11-28T14:31:40.728Z",
"link": "http://placeimg.com/640/480",
"title": "Et necessitatibus",
"platform": "Repellendus"
}
]
}
}
}
]
},
{
"_id": {
"$oid": "61acefe999e03b9324caaaaa"
},
"matchId": {
"$oid": "61a392cc54e3752cc71zzzzz"
},
"logs": [....]
}
a brief breakdown: I have many objects like this one in the collection. they are a kind of an audit log for actions takes on other documents, 'Match(es)'. for example CREATE + the data, UPDATE + the data, etc.
As you can see, logs field of the document is an array of objects, each describing one of these actions.
data for each action may or may not contain specific fields, that in turn can also be an array of objects: socialLinks and webResults.
I'm trying to remove sensitive data from all of these documents with specified Match ids.
For each document, I want to go over the logs array field, and change the value of specific fields only if they exist, for example: change firstName to *****, same for lastName, if those appear. also, go over the socialLinks array if exists, and for each element inside it, if a field url exists, change it to ***** as well.
What I've tried so far are many minor variations for this query:
$set: {
'logs.$[].data.talent.socialLinks.$[].url': '*****',
'logs.$[].data.talent.webResults.$[].link': '*****',
'logs.$[].data.talent.webResults.$[].title': '*****',
'logs.$[].data.talent.firstName': '*****',
'logs.$[].data.talent.lastName': '*****',
},
and some play around with this kind of aggregation query:
[{
$set: {
'talent.socialLinks.$[el].url': {
$cond: [{ $ne: ['el.url', null] },'*****', undefined],
},
},
}]
resulting in errors like: message: "The path 'logs.0.data.talent.socialLinks' must exist in the document in order to apply array updates.",
But I just cant get it to work... :(
Would love an explanation on how to exactly achieve this kind of set-only-if-exists behaviour.
A working example would also be much appreciated, thx.
Would suggest using $\[<indentifier>\] (filtered positional operator) and arrayFilters to update the nested document(s) in the array field.
In arrayFilters, with $exists to check the existence of the certain document which matches the condition and to be updated.
db.collection.update({},
{
$set: {
"logs.$[a].data.talent.socialLinks.$[].url": "*****",
"logs.$[b].data.talent.webResults.$[].link": "*****",
"logs.$[b].data.talent.webResults.$[].title": "*****",
"logs.$[c].data.talent.firstName": "*****",
"logs.$[d].data.talent.lastName": "*****",
}
},
{
arrayFilters: [
{
"a.data.talent.socialLinks": {
$exists: true
}
},
{
"b.data.talent.webResults": {
$exists: true
}
},
{
"c.data.talent.firstName": {
$exists: true
}
},
{
"d.data.talent.lastName": {
$exists: true
}
}
]
})
Sample Mongo Playground

Mongoose hide fields AFTER populating

Supposed I have this schema
class Room {
member_ids: [String]
owner_ids: [String]
}
And two virtual populates members and owners, which map to User schema (custom path, not _id)
I successfully get the data populated with this:
return this.roomModel
.findOne({ id: roomId })
.select('-_id -__v')
.populate('members owners', '-_id -__v')
.exec();
It now returns
{
"member_ids": [
"1",
"2"
],
"owner_ids": [
"1"
],
"owners": [
{
"id": "1",
"name": "User 1"
}
],
"members": [
{
"id": "1",
"name": "User 1"
},
{
"id": "2",
"name": "User 2"
}
]
}
The thing is, I don't want member_ids and owner_ids to end up in my response. I've tried using select('-member_ids -owner_ids') however the response did not have populated data anymore (I guess the select phase happens before the populate phase?). Is there anyway to achieve this, without resorting to manually removing the fields afterwards? Thank you.

Delete sub-document from array in array of sub documents

Let's imagine a mongo collection of - let's say magazines. For some reason, we've ended up storing each issue of the magazine as a separate document. Each article is a subdocument inside an Articles-array, and the authors of each article is represented as a subdocument inside the Writers-array on the Article-subdocument. Only the name and email of the author is stored inside the article, but there is an Writers-array on the magazine level containing more information about each author.
{
"Title": "The Magazine",
"Articles": [
{
"Title": "Mongo Queries 101",
"Summary": ".....",
"Writers": [
{
"Name": "tom",
"Email": "tom#example.com"
},
{
"Name": "anna",
"Email": "anna#example.com"
}
]
},
{
"Title": "Why not SQL instead?",
"Summary": ".....",
"Writers": [
{
"Name": "mike",
"Email": "mike#example.com"
},
{
"Name": "anna",
"Email": "anna#example.com"
}
]
}
],
"Writers": [
{
"Name": "tom",
"Email": "tom#example.com",
"Web": "tom.example.com"
},
{
"Name": "mike",
"Email": "mike#example.com",
"Web": "mike.example.com"
},
{
"Name": "anna",
"Email": "anna#example.com",
"Web": "anna.example.com"
}
]
}
How can one author be completely removed from a magazines?
Finding magazines where the unwanted author exist is quite easy. The problem is pulling the author out of all the sub documents.
MongoDB 3.6 introduces some new placeholder operators, $[] and $[<identity>], and I suspect these could be used with either $pull or $pullAll, but so far, I haven't had any success.
Is it possible to do this in one go? Or at least no more than two? One query for removing the author from all the articles, and one for removing the biography from the magazine?
You can try below query.
db.col.update(
{},
{"$pull":{
"Articles.$[].Writers":{"Name": "tom","Email": "tom#example.com"},
"Writers":{"Name": "tom","Email": "tom#example.com"}
}},
{"multi":true}
);

Query inner ID in MongoDB

I have two collections
1)User -> With fields,
name as String
emailId as String
2)
Rating -> With fields,
`userId as String.` (This will be the ID of the user and Foreign Key as per SQL)
comment as String`
I have created a record for the user which looks like
{
"_id": {
"$oid": "565fe1294a27a93449751a9a"
},
"name": "Some name",
"email": "somemail#gmail.com",
"createdAt": {
"$date": "2015-12-03T06:28:57.904Z"
},
"updatedAt": {
"$date": "2015-12-03T06:28:57.904Z"
}
}
I have create a record for the Rating which looks like
{
"_id": {
"$oid": "565fefa30878764428d96be1"
},
"userId": "565fe1294a27a93449751a9a",
"comment": "just a test comment",
"createdAt": {
"$date": "2015-12-03T07:30:43.409Z"
},
"updatedAt": {
"$date": "2015-12-03T07:30:43.409Z"
}
}
Now I want to make a query where all the rating done by the user along with the user document are returned.
If I make a query like
db.user.find({
"userId" :"565fe1294a27a93449751a9a"
})
I get the result like
{
"id": "565fefa30878764428d96be1",
"userId": "565fe1294a27a93449751a9a",
"comment": "just a test comment"
}
But I want the user object as well in it something like.
{
"id": "565fefa30878764428d96be1",
"user": { "name": "Some name",
"email": "somemail#gmail.com",
"id": "565fe1294a27a93449751a9a"
},
"comment": "just a test comment"
}
Or even something like this will work as well
"rating": {
"id": "565fefa30878764428d96be1",
"userId": "565fe1294a27a93449751a9a",
"comment": "just a test comment"
},
"user": {
"id": "565fefa30878764428d96be1",
"userId": "565fe1294a27a93449751a9a",
"comment": "just a test comment"
}
Here you need to change schema. You need to change user type from string to reference. If you add reference of User in rating, then that will be easy. If you add reference of User schema then you can populate user on rating. Then will get user info with rating.
example of reference :
User: {type: mongoose.Schema.ObjectId, ref: 'User'}
Found my exact solution here. http://sailsjs.org/documentation/concepts/models-and-orm/associations/one-to-many
Technically it creates two queries in the backend. Found this out by the response time.
But anyways it solves my problem.

How can I query an indexed object list in mongodb?

I have some documents in the "company" collection structured this way :
[
{
"company_name": "Company 1",
"contacts": {
"main": {
"email": "main#company1.com",
"name": "Mainuser"
},
"store1": {
"email": "store1#company1.com",
"name": "Store1 user"
},
"store2": {
"email": "store2#company1.com",
"name": "Store2 user"
}
}
},
{
"company_name": "Company 2",
"contacts": {
"main": {
"email": "main#company2.com",
"name": "Mainuser"
},
"store1": {
"email": "store1#company2.com",
"name": "Store1 user"
},
"store2": {
"email": "store2#company2.com",
"name": "Store2 user"
}
}
}
]
I'm trying to retrieve the doc that have store1#company2.com as a contact but cannot find how to query a specific value of a specific propertie of an "indexed" list of objects.
My feeling is that the contacts lists should not not be indexed resulting in the following structure :
{
"company_name": "Company 1",
"contacts": [
{
"email": "main#company1.com",
"name": "Mainuser",
"label": "main"
},
{
"email": "store1#company1.com",
"name": "Store1 user",
"label": "store1"
},
{
"email": "store2#company1.com",
"name": "Store2 user",
"label": "store2"
}
]
}
This way I can retrieve matching documents through the following request :
db.company.find({"contacts.email":"main#company1.com"})
But is there anyway to do a similar request on document using the previous structure ?
Thanks a lot for your answers!
P.S. : same question for documents structured this way :
{
"company_name": "Company 1",
"contacts": {
"0": {
"email": "main#company1.com",
"name": "Mainuser"
},
"4": {
"email": "store1#company1.com",
"name": "Store1 user"
},
"1": {
"email": "store2#company1.com",
"name": "Store2 user"
}
}
}
Short answer: yes, they can be queried but it's probably not what you want and it's not going to be really efficient.
The document structure in the first and third block is basically the same - you have an embedded document. The only difference between are the name of the keys in the contacts object.
To query document with that kind of structure you will have to do a query like this:
db.company.find({ $or : [
{"contacts.main.email":"main#company1.com"},
{"contacts.store1.email":"main#company1.com"},
{"contacts.store2.email":"main#company1.com"}
]});
This query will not be efficient, especially if you have a lot of keys in the contacts object. Also, creating a query will be unnecessarily difficult and error prone.
The second document structure, with an array of embedded objects, is optimal. You can create a multikey index on the contacts array which will make your query faster. The bonus is that you can use a short and simple query.
I think the easiest is really to shape your document using the structure describe in your 2nd example : (I have not fixed the JSON)
{
"company_name": "Company 1",
"contacts":{[
{"email":"main#company1.com","name":"Mainuser", "label": "main", ...}
{"email":"store1#company1.com","name":"Store1 user", "label": "store1",...}
{"email":"store2#company1.com","name":"Store2 user", "label": "store2",...}
]}
}
like that you can easily query on email independently of the "label".
So if you really want to use the other structure, (but you need to fix the JSON too) you will have to write more complex code/aggregation pipeline, since we do not know the name and number of attributes when querying the system. Theses structures are also probably hard to use by the developers independently of MongoDB queries.
Since it was not clear let me show what I have in mind
db.company.save(
{
"company_name": "Company 1",
"contacts":[
{"email":"main#company1.com","name":"Mainuser", "label": "main"},
{"email":"store1#company1.com","name":"Store1 user", "label": "store1"},
{"email":"store2#company1.com","name":"Store2 user", "label": "store2"}
]
}
);
db.company.save(
{
"company_name": "Company 2",
"contacts":[
{"email":"main#company2.com","name":"Mainuser", "label": "main"},
{"email":"store1#company2.com","name":"Store1 user", "label": "store1"},
{"email":"store2#company2.com","name":"Store2 user", "label": "store2"}
]
}
);
db.company.ensureIndex( { "contacts.email" : 1 } );
db.company.find( { "contacts.email" : "store1#company2.com" } );
This allows you to store many emails, and query with an index.