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
Related
I have a model Book with a field "tags" which is of type array of String / GraphQLString.
Currently, I'm able to query the tags for each book.
{
books {
id
tags
}
}
and I get the result:
{
"data": {
"books": [
{
"id": "631664448cb20310bc25c89d",
"tags": [
"database",
"middle-layer"
]
},
{
"id": "6316945f8995f05ac71d3b22",
"tags": [
"relational",
"database"
]
},
]
}
}
I want to write a RootQuery where I can fetch all unique tags across all books. This is how far I am (which is not too much):
tags: {
type: new GraphQLList(GraphQLString),
resolve(parent, args) {
Book.find({}) // CAN'T FIGURE OUT WHAT TO DO HERE
return [];
}
}
Basically, I'm trying to fetch all books and then potentially merge all tags fields on each book.
I expect that if I query:
{
tags
}
I would get
["relational", "database", "middle-layer"]
I am just starting with Mongoose, MongoDB, as well as GraphQL, so not 100% sure what keywords to exactly look fo or even what the title of this question should be.
Appreciate the help.
You want to $unwind the arrays so they're flat, at that point we can just use $group to get unique values. like so:
db.collection.aggregate([
{
"$unwind": "$data.books"
},
{
"$unwind": "$data.books.tags"
},
{
$group: {
_id: "$data.books.tags"
}
}
])
Mongo Playground
MongoDb + JavaScript Solution
tags = Book.aggregate([
{
$project: {
tags: 1,
_id: 0,
}
},
])
This returns an array of objects that contain only the tags value. $project is staging this item in the aggregation pipeline by selecting keys to include, denoted by 1 or 0. _id is added by default so it needs to be explicitly excluded.
Then take the tags array that looks like this:
[
{
"tags": [
"database",
"middle-layer"
]
},
{
"tags": [
"relational",
"database"
]
}
]
And reduce it to be one unified array, then make it into a javascript Set, which will exclude duplicates by default. I convert it back to an Array at the end, if you need to perform array methods on it, or write back to the DB.
let allTags = tags.reduce((total, curr) => [...total, ...curr.tags], [])
allTags = Array.from(new Set(allTags))
const tags = [
{
"tags": [
"database",
"middle-layer"
]
},
{
"tags": [
"relational",
"database"
]
}
]
let allTags = tags.reduce((total, curr) => [...total, ...curr.tags], [])
allTags = Array.from(new Set(allTags))
console.log(allTags)
Pure MongoDB Solution
Book.aggregate([
{
$unwind: "$tags"
},
{
$group: {
_id: "_id",
tags: {
"$addToSet": "$tags"
}
}
},
{
$project: {
tags: 1,
_id: 0,
}
}
])
Steps in Aggregation Pipeline
$unwind
Creates a new Mongo Document for each tag in tags
$group
Merges the individual tags into a set called tags
Sets are required to be have unique values and will exclude duplicates by default
_id is a required field
_id will be excluded from the final aggregation so it doesn't matter what it is
$project
Chooses which fields to pull from the previous step in the pipeline
Using it here to exclude _id from the results
Output
[
{
"tags": [
"database",
"middle-layer",
"relational"
]
}
]
Mongo Playground Demo
While this solution gets the result with purely Mongo queries, the resulting output is nested and still requires traversal to get to desired fields. I do not know of a way to replace the root with a list of string values in an aggregation pipeline. So at the end of the day, JavaScript is still required.
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();
I have a fetch function which returns a json file. The source file looks like this:
{
"pages":[
{
"page": "people.html",
"title": "People"
},
{
"page": "client.html",
"title": "Client"
}
]
}
When the response from the fetch (after applying .json()) it is a array of multiple objectives, each with two strings:
0
title: "People Policies"
page: "people.html"
1
title: "Client Policies"
page: "client.html"
where 0 and 1 are OBJECTS and title/page the strings.
Question: how can I extract the objects into a simple array (not objects) with multiple key pairs or is there another way to format the source file?
There are plenty of questions on here with similar questions but I just can't get any of them to work, including Object.entries(obj)
Thanks.
From what I understood from your question is that your source data is an array of objects each having two string values. And after .json(), you're getting an indexed object like
{
0: {
// ....
}
}
In this case, you can try Object.values() like
fetch(some_url).then(resp => resp.json()).then(resp => {
// resp is your object
let formattedArray = Object.values(resp);
console.log(formattedArray)
})
const data = {
"pages":[
{
"page": "people.html",
"title": "People"
},
{
"page": "client.html",
"title": "Client"
}
]
}
const pages = data.pages.map(obj => obj.pages)
const titles = data.pages.map(obj => obj.title)
Not sure which one you wanted in an array so heres both.
const json = {
"pages":[
{
"page": "people.html",
"title": "People"
},
{
"page": "client.html",
"title": "Client"
}
]
}
const addObj = json.pages
for (el of json.pages) {
console.log('key-value pairs:', Object.entries(el), '; keys:', Object.keys(el), '; values:', Object.values(el))
}
I'm using mongoose with node.js.
Let's say I have 'Posts' DB where each document in it is a post.
Each post has a 'ReadBy' array which holds names of users that had read this post.
When I'm searching for documents in this DB, I want to "change" the 'ReadBy' value to show by Boolean value if the user that is searching for it is in this array or not.
For example, let's say these are 2 documents that are in this DB:
{ "PostName": "Post Number 1", "ReadBy": ["Tom", "John", "Adam"] }
{ "PostName": "Post Number 2", "ReadBy": ["John", "Adam"] }
If I'm user 'Tom', I want to get the results like this:
[
{
"PostName": "Post Number 1",
"ReadBy": true,
},
{
"PostName": "Post Number 2",
"ReadBy": false,
}
]
Now, I know that I can get the documents and go over each one of them with forEach function, and then use forEach again on the "ReadBy" array and change this field.
I'm asking if there is more efficient way to do it in the mongoDB query itself, or some other way in the code.
If there is another way with mongoose - even better.
Using mongoDb $setIntersection in aggregation you get the result like this :
db.collectionName.aggregate({
"$project": {
"ReadBy": {
"$cond": {
"if": {
"$eq": [{
"$setIntersection": ["$ReadBy", ["Tom"]]
},
["Tom"]
]
},
"then": true,
"else": false
}
},
"PostName": 1
}
})
So above working first like this
{ $setIntersection: [ [ "Tom", "John", "Adam"], [ "Tom"] ] }, return [ "Tom"]
{ $setIntersection: [ [ "John", "Adam"], [ "Tom"] ] }, return [ ]
and $eq to check whether setIntersection results matched with ["Tom"] if yes then return true else false
You can try something similar to
var unwind = {"$unwind": "$ReadBy"}
var eq = {$eq: ["$ReadBy", "Bob"]}
var project = {$project: {PostName: 1, seen: eq}}
db.posts.aggregate([unwind, project])
Just notice that you solution is highly inefficient. Both for storing the data ( growing array) and for searching.
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.