Firestore - Combine documents from two different collection - React Typescript - reactjs

I have created three collections because I want to be able to do filter search by tasks or users etc
My Firebase collection looks like this:
Collection 1 - (Users)
- username
- name
Collection 2 - (Tasks)
- taskname
- points
Collection 3 - (taskData)
- userId (id from users collection)
- taskId (id from task collection)
- timestamp
React Typescript - Fetch collection data
interface UserTask {
id: String;
taskId: String;
userId: String;
date: Date;
}
const [userTasks, setUserTasks] = useState<UserTask[]>([]);
const getTaskData = () => {
const taskRef = firestore.collection('taskData');
taskRef.get().then((snapshot) => {
const taskData = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data()
}))
console.log(taskData)
})
}
I am able to get the TaskData which has userId and TaskId but have not been able to "join" the tables and get Users.name and Tasks.taskname.I want to get a output like below, How do I do that?
{
"taskName1": [
{
"name": "John",
"date": "12.01.2002"
},
{
"name": "Maya",
"date": "15.01.2002"
},
{
"name": "Hanna",
"date": "13.01.2003"
}
],
"taskName2": [
{
"name": "Maya",
"date": "20.04.2003"
},
{
"name": "Maya",
"date": "17.06.2003"
}
],
"taskName3": [
{
"name": "John",
"date": "21.05.2003"
}
]
}

Related

Retrieving related elements from MongoDB

I have the following data in MongoDB. Based alone on an id that I have available how can I retrieve all other entries where the player matches the player for my current id.
For example : find who the player for id 12 is, search all other entries that match that player name and return a list of all of them.
[
{_id: '62ecdf342f1193134043964c', id: '12', player: 'David Beckham', team: 'Manchester United'},
{_id: '62ecdf342f1193134043965c', id: '17', player: 'Cristiano Rolando', team: 'Manchester United'},
{_id: '62ecdf342f1193134043966c', id: '22', player: 'Cristiano Rolando', team: 'Juventus'},
{_id: '62ecdf342f1193134043967c', id: '42', player: 'David Beckham', team: 'Real Madrid'},
]
This is the code that I'm using to retrieve the one single entry that matches a specific id and then I'd also like to get the related entries.
export async function getStaticProps({ params }) {
const { db } = await connectToDatabase();
const jerseyA = await db
.collection("Jerseys")
.find({ id: params.jersey })
.sort()
.toArray();
const jersey = JSON.parse(JSON.stringify(jerseyA))[0];
return { props: { jersey } };
}
Now that you know the name, do another fetch like .find({player: jersey.player})
I'm not sure of the output format you want, but here's one way to return all documents that match the "player" name of the given "id".
db.Jerseys.aggregate([
{
"$match": {
// your id goes here
"id": "17"
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{"$unwind": "$docs"},
{"$replaceWith": "$docs"}
])
Example output:
[
{
"_id": "62ecdf342f1193134043965c",
"id": "17",
"player": "Cristiano Rolando",
"team": "Manchester United"
},
{
"_id": "62ecdf342f1193134043966c",
"id": "22",
"player": "Cristiano Rolando",
"team": "Juventus"
}
]
Try it on mongoplayground.net.
Just an addition to #rickhg12hs solution, to ignore the first record. You can use the following query to ignore the first record (where the id also matched) and the others.
db.Jerseys.aggregate([
{
"$match": {
"id": "12"
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{
"$unwind": "$docs"
},
{
"$replaceWith": "$docs"
},
{
"$match": {
"id": {
"$not": {
"$eq": "12"
}
}
}
}
])
A possible javascript translation of it, should be,
export async function getStaticProps({ params }) {
const { db } = await connectToDatabase();
const { jersey: id } = params;
const jerseyA = await db
.collection("Jerseys")
.aggregate([
{
"$match": {
id
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{
"$unwind": "$docs"
},
{
"$replaceWith": "$docs"
},
{
"$match": {
"id": {
"$not": {
"$eq": id
}
}
}
}
]).toArray();
const jersey = JSON.parse(JSON.stringify(jerseyA))[0];
return { props: { jersey } };
}

How to create nested array in realm without key(React Native)

{
"a": [
[
{
"_id": "57e55b64016c3551c025abc1",
"title": "Main Campus"
},
{
"_id": "5810e2e27064497f74ad4874",
"title": "Ahm Campus"
},
{
"_id": "5d5d2633a1d0680620ac3cce",
"title": "Baroda"
},
{
"_id": "5d5d3af3a1d0680620ac3ef8",
"title": "India"
}
],
[
{
"_id": "57e55b64016c3551c025abc1",
"title": "Main Campus"
},
{
"_id": "5810e2e27064497f74ad4874",
"title": "Ahm Campus"
},
{
"_id": "5d5d2633a1d0680620ac3cce",
"title": "Baroda"
},
{
"_id": "5d5d3af3a1d0680620ac3ef8",
"title": "India"
}
]
]
}
How to create the schema in the realm(React native) for this type of JSON object. I tried all possible ways but did not found any specific solution. Basically, it is a nested array where the second array does not have any specific key(I tried with key it works fine but I want to do it without adding key).
You can use something like:
const ParentSchema = {
name: "parent",
properties: {
key: "string",
values: "Value[]"
}
};
const ValueSchema = {
name: "Value",
embedded: true,
properties: {
_id: "string",
title: "string"
}
};
You can insert objects like:
realm.write(() => {
realm.create("Parent", { key: "a", values: [
{ _id: "57e55b64016c3551c025abc1", title: "Main Campus" },
{ _id: "5810e2e27064497f74ad4874", title: "Ahm Campus" }
]
});
});
Documentation: https://docs.mongodb.com/realm/node/data-model
As of now there is no way to insert direct value in Realm database without key so for now we need to modify data and then we can store in following schema.
const ParentSchema = {
name: "parent",
properties: {
a: "level[]"
}
};
const level = {
name: 'level',
properties: {
level: 'sites[]'
}
}
const sites = {
name: 'sites',
properties: {
sites: 'site[]'
}
}
const site = {
name: 'site',
properties: {
title: 'string?',
_id: 'string?',
version: 'int?',
}
}
Data modification need to done like following.
var a = {
level: []
}
data.a.map((Site, index) => {
const sites = []
Site.map((s) => { sites.push(s)})
a.level.push({sites})
})

Cannot log data from JSON-server API with GraphQL

I have built an API with JSON-server and I am trying to fetch the data from it using React-Apollo Client.
I'm trying to log the data from API on the console with Query tag, restructure and print the data variable using console.log().
I have no idea why the function is getting print via console.log().
I have the current setup:
JSON server is running on PORT 4000
Server is running on PORT 5000
Client is running on PORT 3000
I am already using CORS tool
Below is my component:
const BOOKS_QUERY = gql`
query BooksQuery {
books {
title
author
editionYear
}
}
`;
<Query query={BOOKS_QUERY}>
{({ loading, error, data }) => {
if (loading) return <h4>Loading...</h4>;
if (error) console.log(error);
console.log(data);
return <h1>test</h1>;
}}
</Query>
The content below is code for my schema:
const BookType = new GraphQLObjectType({
name: 'Book',
fields: () => ({
id: { type: GraphQLInt },
title: { type: GraphQLString },
author: { type: GraphQLString },
editionYear: { type: GraphQLInt }
})
});
//Root Query
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
books: {
type: new GraphQLList(BookType),
resolve(parent, args) {
return axios.get('http://localhost:4000/books').then((res) => res.data);
}
},
book: {
type: BookType,
args: {
id: { type: GraphQLInt }
},
resolve(parent, args) {
return axios.get(`http://localhost:4000/books/${args.id}`).then((res) => res.data);
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery
});
API:
{
"books": [
{
"id": "1",
"title": "Java How To Program",
"author": "Deitel & Deitel",
"editionYear": "2007"
},
{
"id": "2",
"title": "Patterns of Enterprise Application Architecture",
"author": "Martin Fowler",
"editionYear": "2002"
},
{
"id": "3",
"title": "Head First Design Patterns",
"author": "Elisabeth Freeman",
"editionYear": "2004"
},
{
"id": "4",
"title": "Internet & World Wide Web: How to Program",
"author": "Deitel & Deitel",
"editionYear": "2007"
}
]
}
I only expect the API data to be logged on console.
Later I will render that data on screen.

How can I mutate my state without having to reload the entire json object via the API

I am using reactjs with redux, and on the UI I am displaying categories along with the products associated to them.
When the user moves a product to another category, I don't want to have to reload the entire JSON file as I am doing currently because the JSON is actually pretty large.
How can I mutate this JSON file by just removing the productId from the catProducts collection of one category and adding the productId to another categories catProducts collection.
{
"shop" : {
"categories": [
{
id: 1,
name: "category1",
"catProducts" : [
{
productId: 123
}
]
},
{
id: 2,
name: "category2",
"catProducts" : [
{
productId: 456
}
]
},
{
id: 3,
name: "category3",
"catProducts" : [
{
productId: 789
}
]
}
]
}
}
Below is my action and reducer, as you can see I am reloading the entire category with products each time a product is moved. All I need to do is after the API call is made, mutate my state by removing the productId from one catProducts and add it to another. How can I do this?
Actions:
moveProduct: (data) => {
return dispatch => {
var url = '....'; // api to move the product in db
axios.post(url, reqArgs)
.then(function(resp) {
dispatch(Actions.fetchCategoriesAndProducts(data.shopId));
});
};
},
Reducer:
case Constants.FETCH_CATEGORIES_AND_PRODUCTS:
return {...state, fetching: false, shop: action.data };
Given that you have an index fromIndex representing the categories array index to move a product from, toIndex representing the categories array index to move a product to, and productId of the product to move, you could do it like this:
const data = {
shop: {
categories: [
{
id: 1,
name: "category1",
catProducts: [
{
productId: 123
}
]
},
{
id: 2,
name: "category2",
catProducts: [
{
productId: 456
}
]
},
{
id: 3,
name: "category3",
catProducts: [
{
productId: 789
}
]
}
]
}
};
const fromCategoryIndex = 0;
const toCategoryIndex = 1;
const productId = 123;
const categories = [...data.shop.categories];
categories[fromCategoryIndex] = {
...categories[fromCategoryIndex],
catProducts: categories[fromCategoryIndex].catProducts.filter(
product => product.productId !== productId
)
};
categories[toCategoryIndex] = {
...categories[toCategoryIndex],
catProducts: [...categories[toCategoryIndex].catProducts, { productId }]
};
console.log(categories);

updating a JSON array in AWS dynamoDB

My document looks like this:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
Say, I need to update the attendee JSON array with attendeeId: 2016102973634-df. I tried many ways ways using update and condition expression, but no success.
Here is my try:
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[???] = ",
ConditionExpression: attendees.attendeeId = "2016102973634-df",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});
Could not find any resources for updating an Json in a array.
After #notionquest's comment:
- Have not used any JsonMarshaller. Initially I added the empty array to attendees field like this:
{
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": []
}
and then When a new attendee comes I add it to the attendees property like this:
const attendee = {
"attendeeName": "user1",
"personalizedDateSelection": {"today": "free"}
}
const attendeeList = [attendee];
const eventId = "20161029125458-df-d";
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees = list_append(attendees, :attendee)",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log("in update dynamo");
console.log(data.Attributes);
});
As you have seen in the above snippets, initially I add empty [] array and add a new attendee using the above code. Now, How do I update a specific JSON in an array. If you say that is not possible, what else can I try?
Should I try this :
Get the Full JSON.
Manipulate the JSOn and change the things I want in my nodeJS.
And then update the new JSON to dynamoDB.
But this consumes two calls to dynamoDB which seems to be inefficient.
Would like to know If there is any round way ?
you can store the index of list. while updating the list we can use them. For example ,
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"index":0,
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"index":1,
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[attendee.index].attendeeName = :value",
ExpressionAttributeValues: {
":value" : {"S":"karthik"}
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});
An example of an update query:
Data structure (saved in DynamoDB)
{
tenant_id: 'tenant_1',
users: {
user1: {
_id: 'user1',
email_address: 'test_email_1#gmail.com'
},
user2: {
_id: 'user2',
email_address: 'test_email_2#gmail.com'
}
}
}
Data for update (used in the params)
var user = {
email_address: 'updated#gmail.com'
}
Params
var params = {
TableName: 'tenant-Master',
Key: {
"tenant_id": 'tenant_1'
},
UpdateExpression: "set #users.user1 = :value",
ExpressionAttributeNames: {
"#users": "users"
},
ExpressionAttributeValues: {
":value": user,
},
};
Explanation
By switching to a map of maps from an array of maps we can now use UpdateExpression: "set #users.user1 = :value" to update our nested object at the map of users with the id of user1.
NOTE: This method as is will REPLACE the entire map object at users.user1. Some changes will need to be made if you want to keep pre-existing data.
I could not find any answer to query and update the JSON-array. I think this may be AWS profitable motive to not allow those features. If you need to query on a particular ID other than primary key, you need to make a secondary index which is cost effective. This secondary index cost is additional to the dyn
amoDB table cost.
Since, I did not want to pay extra bucks on secondary index, I changed my dynamoDB schema to the following:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": {
"2016102973634-df": {
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
"2016102973777-df": {
"attendeeId": "2016102973777-df",
"attendeeName": "ffff",
"personalizedDateSelection": {}
}
}
}
}
Changing attendees from [] to {}. This allows me the flexibility to query particular attendeeId and change the entire JSON associated with that. Even though, this is a redundant step, I do not want to spend extra bucks on my hobby project.

Resources