I have an array of elements which contain some ids. I need to connect to firestore and fetch only the records which have each specified id.
My array:
var ids = { 101, 201, 303}
and my firestore documents:
{
"users": {
{
"id": 1,
"name": "name1",
"otherData:" "otherData"
},
{
"id": 2,
"name": "name1",
"otherData:" "otherData"
},
{
"id": 3,
"name": "name1",
"otherData:" "otherData"
},
...
{
"id": 1000,
"name": "name1",
"otherData:" "otherData"
}
}
}
How can I do that efficiently using db.collection('coll1').where() statements?
I have tried to fetch the data using forEach like this:
ids.forEach(id => {
let result = db.collection('coll1').where('id', '==', id).get();
...
});
But each time I try doing it this way, it does not work.
I am new to the firestore environment and not sure how to do such an operation. Please help.
You can use Firstore compound queries for this. Link to officials docs here.
Use the in operator to combine up to 10 equality (==) clauses on the same field with a logical OR. An in query returns documents where the given field matches any of the comparison values.
Similarly, use the array-contains-any operator to combine up to 10 array-contains clauses on the same field with a logical OR. An array-contains-any query returns documents where the given field is an array that contains one or more of the comparison values
You need to change ids to array type
var ids = [ 101, 201, 303 ]
Query
db.collection("coll1").where("id","in", ids).get();
Related
I am working on a MERN project. I have created a collection in MongoDB having different types of document. Is it an accepted practice to have different structure documents in a single collection? Secondly i need to fetch only a single document from the collection using the key name. My documents are
[{
"_id": {
"$oid": "6333f72822dc0acc4bea17bd"
},
"designation": [
{
"name": "Chairman",
"level": 17
},
{
"name": "Director",
"level": 13
},
{
"name": "Secretary ",
"level": 13
},
{
"name": "Account Officer",
"level": 9
},
{
"name": "Data Entry Operator-GR B",
"level": 5
}
]
},
{
"_id": {
"$oid": "6334313b22dc0acc4bea17c2"
},
"storeRole": ["manager", "approver", "accepter", "firstsignatory"]
},
{
"_id": {
"$oid": "63369d2083a7cc2e818990dd"
},
"designationSuffix": ["I","II", "III"]
}]
How do I get any of the three documents if I only know the key name i.e(designation, storeRole, designationSuffix). I dont want to use ID value.
Welcome to SO.
First, yes it is an accepted practice and indeed, a powerful feature of MongoDB to have different shapes of data in a single collection.
There are two important things to remember when querying for data:
Matching on fields that don't even exist in a document is OK; the document will simply be skipped. This permits you, for example, to query for storeRole and ignore the other documents with designation, etc. -- unless of course you wish to look for those too using an $or expression.
Matching (using $match) for elements in an array will return the whole array, not just the elements that match.
To illustrate this point, let's expand your input data slightly:
{"designation": [
{"name": "Chairman","level": 17},
{"name": "Director", "level": 13}
]
},
{"designation": [
{"name": "Secretary","level": 13}
]
},
We will use dot notation to reach into the structures in the designation array to find those docs where at least one of the name fields is Chairman:
db.foo.aggregate([
{$match: {"designation.name": "Chairman"}}
]);
{
"_id" : 0,
"designation" : [
{
"name" : "Chairman",
"level" : 17
},
{
"name" : "Director",
"level" : 13
}
]
}
The query eliminated the document with name = Secretary as expected but properly returned the whole document (and the whole array) where name = Chairman. Very often the goal is to fetch only the matching items in the array; this is accomplished with the $filter operator:
db.foo.aggregate([
{$match: {"designation.name": "Chairman"}},
{$project: {
// Assigning the output of $filter to the same name as input:
designation: {$filter: {
input: "$designation",
as: "zz",
cond: {$eq: ['$$zz.name','Chairman']}
}}
}}
]);
{
"_id" : 0,
"designation" : [
{
"name" : "Chairman",
"level" : 17
}
]
}
An alternative approach which is useful when query conditions yield null or empty arrays instead of eliminating the document altogether is to $filter first, then match only on results where the array has a length > 1. We must use the $ifNull function to protect $size from being passed a null by turning it into an empty (but not null) array:
db.foo.aggregate([
{$project: {
// Assigning the output of $filter to the same name as input:
designation: {$filter: {
input: "$designation",
as: "zz",
cond: {$eq: ['$$zz.name','Chairman']}
}}
}},
{$match: {$expr: {$gt:[{$size: {$ifNull:["$designation",[] ]}}, 0]}} }
]);
Try commenting out the $match to see what $filter returns when a document has the target array field but no matches vs. when the document does not have the field.
Apologies for the nood question. I'm just starting out using MongoDB and MongoDB Shell.
I've got a DB called Dealers that looks a little like this (very simplified):
[
{
"Id": 1,
"Vehicles": [
{
"Manufacturer": "Ford"
},
{
"Manufacturer": "MG"
},
{
"Manufacturer": "Citroen"
}
]
},
{
"Id": 2,
"Vehicles": [
{
"Manufacturer": "Ford"
},
{
"Manufacturer": "Nissan"
},
{
"Manufacturer": "Ford"
}
]
}
]
I'm trying to get my head round how you filter collections within collections EG. Say I wanted to select all the Ford's from Id 2.
I get as far as:
const dealer = database.collection('Dealers');
const result = await dealer.find({Id: 2})
and I tried:
const result = await dealers.find({
Id: 2,
Vehicles: [
{
Manufacturer: "Ford"
}
]
})
But I know that won't work because it's not iterating through the Vehicles collection. Is this the sort of instance that you would use an aggregation? Like I say, I'm very new to this sort of environment, and would really appreciate any pointers please.
I Just have tried. You can use Aggregate function To actually match the items inside the array in the collection. Like the way following query will select all the documents that have id equal to 1 and Manufacturer equal to Ford
db.MyCollection.aggregate([{$match:{Id:1}},{$unwind:"$Vehicles"}, {$match: {"Vehicles.Manufacturer":"Ford"}}]);
It is returning like this. I have used my own Id i.e equal to one you can change this.
I'm trying to fetch documents from cloudant db with node js. Here for one single input value I'm able to get results, but with array of input's I'm confusing to write query.
This is my node js query part for single input:
var query = {
"selector":
{
"name": 'name1',
},
"fields": [
"_id",
"_rev",
"subjects",
"name"
],
};
For array of names:
var query = {
"selector":
{
"name": {$in: ['name1', 'name2']},
}
};
Here for name field input, i want to pass multiple names in an array. So that what ever names match those documents should return and the above query for multiple names is not working. Any help or suggestion will be appreciated.
If you want to match documents with either name1 or name2, then use the $or combination operator e.g.
var query = {
"selector":
{
"$or": [
{ "name": "name1" },
{ "name": "name2" }
]
}
};
I dont think there is $in operator but there is $all which will match array of values
Try
'$all': ['name1', 'name2']
Im trying to fetch single entry from my table that contains and JSONB array of objects. Can I match somehow that array to find the desired result?
[
{
"chats": [
{
"id": 56789,
},
{
"id": 66753,
},
],
"id": 999
},
{
"chats": [
{
"id": 43532,
}
],
"id": 999
}
]
I would like to get the object that matches id 999 and contains in chats -> id: 66753
Tried few approaches but none worked.
I though something link this will work.But no success
let { data, error } = await supabase
.from('xyz')
.select('*')
.eq('id', 999)
.contains('chats', {id: 66753})
Can it be done ?
I believe you need to use the ->> operator when querying JSONB data from supabase as noted here in the docs.
So, if your column with the array of objects is titled jsonb, something to the effect of:
let { data, error } = await supabase
.from('xyz')
.select('*')
.eq('id:jsonb->>id', 999)
.contains('chats:jsonb->>chats', ['chats->id: 66753'])
More info can be found on PostgREST docs here
I have a BSON object like this saved in MongoDB:
{
"title": "Chemistry",
"_id": "532d665f89ae4ae703b29730",
"__v": 0,
"sections": [
{
"week": 1,
"_id": "532d665f89ae4ae703b29731",
"assignments": [
{
"created_date": "2014-03-22T10:30:55.621Z",
"_id": "532d665f89ae4ae703b29733",
"questions": []
},
{
"created_date": "2014-03-22T10:30:55.621Z",
"_id": "532d665f89ae4ae703b29732",
"questions": []
}
],
"materials": []
}
],
"instructor_ids": [],
"student_ids": []
}
What I wish to do is to retrieve the 'assignment' with _id 532d665f89ae4ae703b29731. It is an element in the assignments array, which, in turn, is an element in the sections array.
I am able to retrieve the entire document with the query
{ 'sections.assignments._id' : assignmentId }
However, what I want is just the assignment subdocument
{
"created_date": "2014-03-22T10:30:55.621Z",
"_id": "532d665f89ae4ae703b29733",
"questions": []
}
Is there a way to accomplish such query? Should I resolve to have assignment in a different collection?
As of mongoose version 6.x, the accepted answer is not valid any more because $elemMatch cannot be used any more on nested documents, instead, aggregate should be used.
if you want ti use an _id to find the document you should convert the _id you get as argument to native mongoDb _id format otherwise it will be constructed as a string and an error will occur.
const native_id = mongoose.Types.ObjectId(id);
const assignment = await <your_model_here>.aggregate([
{ $unwind: "$sections" },
{ $unwind: "$sections.assignments" },
{ $match: { "sections.assignments._id": native_id } },
{ $project: { _id: true, sections: "$sections.assignments" } }
]
)
console.log(assignment) // you have what you want
you can do a aggregate query like this :
db.collection.aggregate(
{$unwind: "$sections"},
{$unwind: "$sections.assignments"},
{$match: {"sections.assignments._id": "532d665f89ae4ae703b29731"}},
{$project: {_id: false, assignments: "$sections.assignments"}}
)
However, I recommends you to think about creating more collections, like you said.
More collections seems to me a better solution then this query.
To retrieve a subset of the elements of an array, you'll need to use the $elemMatch projection operator.
db.collection.find(
{"sections.assignments._id" : assignmentId},
{"sections.assignments":{$elemMatch:{"_id":assignmentId}}}
)
Note:
If multiple elements match the $elemMatch condition, the operator returns the first matching element in the array.