Firestore: query with array comparison - swift

I am trying to achieve things similar to ref.whereField("userID", existsIn: friendArray), which would allow me to filter out friends from a collection of users. I know such query is not supported. A client side solution such as db.collection("users").getDocuments() { //iterate documents } might not be very data friendly, is there a server side solution?

Although you didn't show your document structure, I suppose you currently have something like:
{
"user_name": "Jon Doe",
"friendArray": [{
0: "userID",
1: "userID2",
2: "userID3"
}]
}
But the Firestore Docs recommend structuring your data as:
{
"name": "Jon Doe",
"friendArray": {
"userID": true,
"userID2": true,
"userID3": true
}
}
This way, you can retrieve all users who have userID in their list of friends using:
db.collection("users")
.whereField("friendArray.userID", isEqualTo: true)
.getDocuments() { (querySnapshot, err) in
// ...
}

Related

MERN stack, Mongodb updating all embedded document arrays with this value if they have a certain value

I am trying to updateMany() documents which are an array of documents, if they have a certain value. I am still learning mongodb and I know how to update all documents if they have this value with my new value. The problem I think I am having is that they are an array of documents so I need to go into each document find the field called courses go inside of it and find the document called name and then update it the value of name and I have been trying, but no luck. Also In the request I update the first collection no problem its when I get into the next collection where it does nothing like I already mentioned.
request.body.code = request.sanitize(request.body.code);
request.body.name = request.sanitize(request.body.name);
request.body.courses.forEach(course => {
course.code = request.sanitize(course.code);
course.name = request.sanitize(course.name);
});
let newValues = { $set: {"code": request.body.code, "name":request.body.name } };
result = await mongoClient.db(DB_NAME).collection("all_courses").updateOne(selector, newValues);
// Okay now try to update other collection of array documents which isnt working yet
selector = { "code": "PROG2700"};
newValues = { $set: {"code": request.body.code, "name": request.body.name } };
console.log("this is new values");
console.log(request.body.courses);
result = await mongoClient.db(DB_NAME).collection("technologies").updateMany(selector,newValues);
That is in my server doing the mongodb work and trying to update the documents.
let sendString:Object = {
"id": my,
"code": edit_Course?.code,
"name": state_category_name,
"courses":
[
{
"code": edit_Course?.code,
"name": state_category_name
}
]
}
That is what I am sending to the server as the request.body
{
"_id": "5fdaa52060689aa159e4122e",
"name": "Webpack",
"description": "Webpack is great",
"difficulty": 2,
"courses": [
{
"code": "PROG2700",
"name": "Client Side Programming"
},
{
"code": "PROG3017",
"name": "Full Stack Programming"
}
]
}
That is what the mongodb structure of each document looks like in the technologies collection that I am having trouble updating. Its the name field I need to update in every document under courses. Could anyone give me a helping hand, many thanks!
Your selector filter doesn't work for the technologies class because the matching code field is part of an array named courses. To account for this, the filter should look something like this:
{ "courses.code": "PROG2700" }
This is an example of querying a field in a document embedded in an array.
The next step is to update the right document in the array. This can be done in MongoDB using the positional operator $. The update document would look like this:
{ $set: { "courses.$.code": request.body.code, "courses.$.name": request.body.name }}
In this update document, the positional operator tells MongoDB to update the code and name fields of the first document in the courses array with a code of PROG2700.
Put together, the update might look like this:
filter = { "courses.code": "PROG2700" };
update = { $set: { "courses.$.code": request.body.code, "courses.$.name": request.body.name }};
result = await mongoClient.db(DB_NAME).collection("technologies").updateMany(filter,update);

mongodb find and update one with multiple conditions

{
roomId: "id",
questions:{
q1:{
user1:"user1's work"
}
}
}
I'm trying to query mongodb with multiple conditions, that roomId has to match, and questions must be q1, and in q1 there must be a user1.
Here's what I've tried so far. Using and operator, but doesn't seems to work.For now I'm using find, as I read in the docs that updateMany has the same query selector as find.
const result = await collection.find({
$and: [
{
roomId: roomId,
},
{
questions: {
currentQuestion: {
userName,
},
},
},
],
});
My schema:
{
roomId: "id",
roomName:"roomName",
questions:{
question1:{
user1:"user1's work",
userN: "userN's work"
},
questionN:{
user1:"",
userN:""
}
}
}
My expected input , (roomId, currentQuestion, userName) for query conditions,"userWork" to be inserted to what's under userName (user1 - userN).
Expected output, that the user's work gets updated with "userWork", under the right room, right currentQuestion and the right user.
You need this query I think:
db.collection.find({
"roomId": "id",
"questions.q1.user1": {
$exists: true
}
})
This query find a roomId which match your 'id' and then, check if exists the element questions.q1.user1.
Mongo playground example here
PS: You said update but... what do you want to update?
Assuming your schema is like
{
roomId: "id",
questions: {
q1: {
user1: "user1's work",
currentQuestion: "currentQuestion1"
}
}
}
Then, the query to update the currentQuestion field whose match the id and has existing questions.q1.user1 is this:
db.collection.update({
"roomId": "id",
"questions.q1.user1": {
$exists: true
}
},
{
"$set": {
"questions.q1.currentQuestion": "new question"
}
})
Example here
Note that if currentQuestion is one level up, you only have to modify $set object.
If you are not asking for this, please provide a complete schema, example input and expected output.

How to search through a list of objects nested inside an array with Spring data MongoDB?

I have got a collection of documents and each documents contains a nested array of objects.
{
"id": "309324739",
"debters": [
{
"user": {
"name": "John Doe",
"internal": true
},
"debt": 1463,
},
{
"user": {
"name": "Alex Tree",
"internal": false
},
"debt": 53443,
},
}
What I'm trying to do is to return find the document by id and then find inside the debters list that has a false flag?
I tried the following query...
Debters findByIdAndDebters_User_InternalIsFalse(#Param("id") String id,);
But I'm getting an error saying that it can find "internal" property. What am I doing wrong and how can I loop through array using this magic mongo repository query?
you need to write a native query for that which is similar to
#Query("{'debters.user.internal':false,'_id':''}")
Debters findByIdAndDebtersUserInternalIsFalse(#Param("id") String id,);

Return Array of Populated Objects in Mongoose

I have a DB for a forum with 3 collections: Threads, Posts, Comments.
I have a GET request to return an individual forum thread that populates each thread with user's posts, and each user post with any comments that were made on it which is working as shown below:
router.get('/:id', (req, res) => {
Threads
.findById(req.params.id)
.lean()
.populate({path: 'posts'})
.exec(function(err, docs){
var options = {
path: 'posts.comments',
model: 'comments'
};
if(err) return res.json(500);
Threads.populate(docs, options, function(err, thread){
res.json(thread);
})
})
})
When this GET request is made it will return a forum thread like so:
{
"_id": "5924ad549a08ed4e70a9c89f",
"title": "Testing Full Schemas",
"author": "Mongoose",
"content": "Schema Content",
"posts": [
{
"_id": "5924ad999a08ed4e70a9c8a0",
"content": "New Schema Post",
"user": "Mongodb",
"comments": [
{
"_id": "5924ae489a08ed4e70a9c8a1",
"comment": "New Schema Content",
"user": "Matt",
"likes": 0,
"created": "2017-05-25T12:41:58.319Z"
}
]
}
Now I need a GET request to return an array of ALL threads (router.get('/')) with each threads posts and comments to be populated. I tried to replace:
Threads
.findById(req.params.id)
with
Threads
.find(req.params.id)
but it is not working. Does anyone have an idea of how this could be accomplished?
To return all the threads, simply use find without any match condition in it.
Also, populate posts and 'posts.comment' in the find query itself, you don't need to do it in the callback of the find.
use population across multiple levels
**Try this:
Threads.find({})
.populate({
path:'posts',
populate :{
path : comments
}
})
.exec(function(err,docs){
//docs is the array of all the Threads, with posts and comments populated within it
})
Read Mongoose Documentation on Populate and Nested Population for detailed information. ( Search for Populating across multiple levels)
findById and findOne returns a single document, where find returns a cursor. Once you go through the cursor of find, you are at the end, and there are no more documents.
Using find query:-
ModelName.find({_id:req.params.id})
.populate({
path:'posts',
populate :{
path : comments
}
},(error,data)=>{
if(error){
res.json(error);
}
else{
res.json(data);
}
})
Using findById query:-
ModelName.findById(req.params.id)
.populate({
path:'posts',
populate :{
path : comments
}
},(error,data)=>{
if(error){
res.json(error);
}
else{
res.json(data);
}
})

Querying grandchild properties in Firebase

I try to come up with a Firebase realtime DB structure for an online store.
The store should have a collection of products, each product can belong to one or more categories. Is it possible to construct a query to get all products in the computers category with a single HTTP request (assuming I use Firebase REST API)? Here is a sample piece of data:
{
"products": {
"-KaXxv2xD9WaIqHMsHYM": {
"title": "Item 1",
"categories": {
"electronics": true,
"computers": true
}
},
"-KaXyvdw5gmuBmGi5unb": {
"title": "Item 2",
"categories": {
"electronics": true
}
},
"-KaXyyyzmP9Y6askhLdx": {
"title": "Item 3",
"categories": {
"computers": true
}
}
}
}
I was also trying to use arrays for categories but looks like arrays support is very limited in Firebase and they should be avoided.
UPDATE:
This query works:
GET /products.json?orderBy="categories/computers"&equalTo=true
But it requires an index for every single category:
{
"rules": {
"products": {
".indexOn": ["categories/computers", "categories/electronics"]
}
}
}
You should have an additional categories node which have products list. That would make easier and efficient access to products for a specific category.
Similar aproach is used at Firebse sample. See code
childUpdates.put("/posts/" + key, postValues);
childUpdates.put("/user-posts/" + userId + "/" + key, postValues);
They have save same data at posts and user-posts nodes.