I have a josn named "update",and it has an embedded list "comments" like this:
{
id: "update/0",
//comments contains elements with type:comment
comments: [{
id:"comment/0"
children:[{
id:"comment/00",
children[...]
}]
}]
}
Questions are:
1, How to remove an item from update's field:comments by ids "update/0" and
"comment/0"?
2, How to remove an item from comment's field:children by ids "update/0","
comment/0" and "comment/00"?
r.table("update").get("update/0").update(function(doc) {
return doc.merge({
comments: doc("comments").concatMap(function(comment) {
return r.branch(
comment("id").eq("comment/0"),
[],
[comment]
)
})
})
})
r.table("update").get("update/0").update(function(doc) {
return doc.merge({
comments: doc("comments").map(function(comment) {
return r.branch(
comment("id").eq("comment/0"),
comment.merge({
children: comment("children").concatMap(function(child) {
return r.branch(
child("id").eq("comment/00"),
[],
[child]
)
})
}),
comment
)
})
})
})
You can also do that with deleteAt, but you should consider splitting your data in multiple tables
See my comment in How to update an item in a embedded list?
Related
I have a vue-multiselect instance that I'm trying to map in a way where I can take the value from the 'contact_data' index of the array I'm passing into the options and store it into an array without the index name. Basically, I need my copyUsers array to look like:
0:
"email#mail.com"
1:
"new#mail.com"
But right now it's:
0:
"contact_data":"email#mail.com"
Anyway, I've used the spread operator on my response.data object which initially got the options into my multiselect, but now I've tried to map the index of my options to only push the value into the array but it's not working and now I'm not even getting any options to show up in my multiselect.
What am I doing wrong here?
<multiselect
v-model="copyUsers"
#search-change="val => searchCopiedUsers(val)"
:options="copyUsersOptions.map(i => i.contact_data)" //changed this from copyUsersOptions
:multiple="true"
:loading="loading"
placeholder="Enter user(s) to be copied"
label="contact_data"
track-by="contact_data">
</multiselect>
data() {
return {
loading: false,
copyUsers: [],
copyUsersOptions: [],
}
},
methods: {
searchCopiedUsers: function(val) {
console.log('searched for', val);
if (val) {
this.loading = true;
axios.get('/ticket/users/search',{params: {query: val}})
.then(response => {
this.copyUsersOptions = [...response.data];
console.info(this.copyUsersOptions);
console.log('here are the users');
console.log(this.copyUsers);
})
.catch(function(error) {
this.loading = false;
// handle error
console.log(error)
})
.finally(function() {
this.loading = false;
})
} else {
this.copyUsersOptions = [];
}
}
}
}
UPDATE: Full object if I dump response.data
1: {contact_data: "test#mail.com"}
2: {contact_data: "johndoe#msn.com"}
3: {contact_data: "res#gmail.com"}
4: {contact_data: "info#globe.net"}
5: {contact_data: "testing#yahoo.com"}
I see what is going on, when I answered you yesterday I did not have a clear understanding of what your data looks like.
Now understanding that your response data looks something like this
{
1: { contact_data: "test#mail.com" },
2: { contact_data: "johndoe#msn.com" },
3: { contact_data: "res#gmail.com" },
4: { contact_data: "info#globe.net" },
5: { contact_data: "testing#yahoo.com" },
}
I can see that it is an object of objects. In order to transform this into an array. We will get an array of all the keys in that object using Object.keys().
From the data given this will return
[ 1, 2, 3, 4, 5]
Then we will use map() to iterate over the array from Object.keys() to create an array of contact_data.
Object.keys(response.data)
.map((key) => response[key].contact_data)
Here is a sandbox
Putting it all together, your Axios call would then become
axios.get('/ticket/users/search',{params: {query: val}})
.then(response => {
this.copyUsersOptions = Object.keys(res.data)
.map((key) =>
response.data[key].contact_data
)
console.info(this.copyUsersOptions);
console.log('here are the users');
console.log(this.copyUsers);
})
My data model is a list with items. Very simple:
{
_id: 1,
name: "List 1",
items: [
{ _id: 2, text: "Item text 1" },
{ _id: 3, text: "Item text 2" }
]
}
Adding a new list with optimistic response works perfectly:
const [addListMutation] = useAddListMutation({
update: (cache, { data }) => {
const cachedLists =
(cache.readQuery<GetAllListsQuery>({
query: GetAllListsDocument,
})?.lists as TList[]) ?? [];
if (data) {
cache.writeQuery({
query: GetAllListsDocument,
data: {
lists: [...cachedLists, data?.list as TList],
},
});
}
},
});
const addList = async (name: string) => {
const list = {
_id: ..new id here,
name,
items: [],
};
const variables: AddListMutationVariables = {
data: list,
};
await addListMutation({
variables,
optimisticResponse: {
list,
},
});
};
This gets reflected immediately in my component using const { loading, data } = useGetAllListsQuery();. data is updated twice; first with the optimistic response and then after the mutation is done. Just like expected.
Now I'm trying to add an item to the list this way:
const [updateListMutation] = useUpdateListMutation({
update: (cache, { data }) => {
const cachedLists =
(cache.readQuery<GetAllListsQuery>(
{
query: GetAllListsDocument,
},
)?.lists as TList[]) ?? [];
if (data?.list) {
// Find existing list to update
const updatedList = data?.list as TList;
const updatedListIndex = cachedLists.findIndex(
(list: TList) => list._id === updatedList._id,
);
// Create a copy of cached lists and replace entire list
// with new list from { data }.
const updatedLists = [...cachedLists];
updatedLists[updatedListIndex] = { ...updatedList };
cache.writeQuery({
query: GetAllListsDocument,
data: {
lists: updatedLists,
},
});
}
}
});
const updateList = async (updatedList: TList) => {
const variables: UpdateListMutationVariables = {
query: {
_id: updatedList._id,
},
set: updatedList,
};
await updateListMutation({
variables,
optimisticResponse: {
list: updatedList,
},
});
};
const addListItem = async (list: TList, text: string) => {
const updatedList = R.clone(list);
updatedList.items.push({
_id: ...new item id here,
text: 'My new list item',
});
await updateList(updatedList);
};
The problem is is in my component and the const { loading, data } = useGetAllListsQuery(); not returning what I expect. When data first changes with the optimistic response it contains an empty list item:
{
_id: 1,
name: "List 1",
items: [{}]
}
And only after the mutation response returns, it populates the items array with the item with text 'My new list item'. So my component first updates when the mutation is finished and not with the optimistic response because it can't figure out to update the array. Don't know why?
(and I have checked that the updatedLists array in writeQuery correctly contains the new item with text 'My new list item' so I'm trying to write the correct data).
Please let me know if you have any hints or solutions.
I've tried playing around with the cache (right now it's just initialized default like new InMemoryCache({})). I can see the cache is normalized with a bunch of List:1, List:2, ... and ListItem:3, ListItem:4, ...
Tried to disable normalization so I only have List:{id} entries. Didn't help. Also tried to add __typename: 'ListItem' to item added, but that only caused the { data } in the update: ... for the optimistic response to be undefined. I have used hours on this now. It should be a fairly simple and common use case what I'm trying to do :).
package.json
"#apollo/client": "^3.3.4",
"graphql": "^15.4.0",
"#graphql-codegen/typescript": "^1.19.0",
I am trying to either add a new field into a returned payload or add a new field copying the contents of another field in the returned payload object. Here is my reducer code...
[actionTypes.GET_PAYTYPE_CONTRIBUTORS]: (state, action) => {
return {...state, paytypeContributors: { ...action.payload }, loadingPaytypeContributors: false, }
},
For each entry in action.payload.Items, I need to either change the field name ID to Id or add Id to the payload Items array with the same contents as the ID field has.
Here is where I tried to do this...
[actionTypes.GET_PAYTYPE_CONTRIBUTORS]: (state, action) => ({...state, paytypeContributors: { ...action.payload, Id: action.payload.Items.ID }, loadingPaytypeContributors: false}),
The payload returns an object and then Items inside of the object is an array and ID is a field in the array. Any ideas on how to do this?
If I understood correctly, that would be my take on this:
const actionFunc = (state, action) => ({ ...state,
paytypeContributors: { ...action.payload,
Items: action.payload.Items.map(Item => {
const newItem = { ...Item,
Id: Item.ID
};
delete newItem['ID'];
return newItem;
})
},
loadingPaytypeContributors: false
});
const state = {};
const action = {
payload: {
Items: [{
ID: 1
},
{
ID: 2
},
{
ID: 3
},
{
ID: 4
}
]
}
}
console.log(actionFunc(state, action));
I noticed the library was greatly improved, but I couldn't find how to do a many to many relationship in the library.
For example, if I have users, and restos. I may have "likes", "visited", "wantToGo" all as different many to many relationships. How would I normalize that in normalizr.js?
There's an example of this in the normalizr repo that you should follow. It requires a combination of a mergeStrategy and processStrategy:
import { schema } from '../../src';
const userProcessStrategy = (value, parent, key) => {
switch (key) {
case 'author':
return { ...value, posts: [parent.id] };
case 'commenter':
return { ...value, comments: [parent.id] };
default:
return { ...value };
}
};
const userMergeStrategy = (entityA, entityB) => {
return {
...entityA,
...entityB,
posts: [...(entityA.posts || []), ...(entityB.posts || [])],
comments: [...(entityA.comments || []), ...(entityB.comments || [])]
};
};
const user = new schema.Entity(
'users',
{},
{
mergeStrategy: userMergeStrategy,
processStrategy: userProcessStrategy
}
);
const comment = new schema.Entity(
'comments',
{
commenter: user
},
{
processStrategy: (value, parent, key) => {
return { ...value, post: parent.id };
}
}
);
const post = new schema.Entity('posts', {
author: user,
comments: [comment]
});
export default [post];
After I delete a post, I want to update the cache and redirect to the post index page.
deletePost() {
this.$apollo.mutate({
mutation: DELETE_POST,
variables: {
postId: this.postId
},
update: (cache, { data: { deletePost } }) => {
const query = {
query: GET_PAGINATED_POSTS,
variables: {
page: 0,
pageSize: 10
},
};
const data = cache.readQuery({ ...query });
data.postsPage = data.postsPage.filter(post => post._id != this.postId)
cache.writeQuery({ ...query, data })
}
})
// redirect
this.$router.push({ name: 'IndexPosts' })
}
The above works, but since I'm not doing an optimisticResponse, there's a bit of a delay between the time the index page shows and the time the cache update takes place. How can I solve this? I was trying to do an optimisticResponse but I don't know how to get the list of paginated posts without doing another query.
this.$apollo.mutate(...) returns a promise.
Try something like:
this.$apollo.mutate(...)
.then(({ data: { deletePost } }) => {
this.$router.push({ name: 'IndexPosts' })
})