how to normalize with redux tool and creactentityAdapter - redux

i using redux tool kit to build react native app and i try to normalize my data like this
const postEntitiy = new schema.Entity('post');
const postAdapter = createEntityAdapter({
selectId: (post) => post._id,
});
const normalized = normalize(response.data, postEntitiy);
this is my resopose.data
Array [
Object {
"__v": 5,
"_id": "6020b367cb94a91c9cd48c34",
"comments": Array [],
"date": "2021-02-08T03:43:35.742Z",
"likes": Array [
Object {
"_id": "60216bd341b3744ce4b13bee",
"user": "601f2d46017c85357800da96",
},
],",
},
]
and this is the error it throw
The entity passed to the `selectId` implementation returned undefined., You should probably provide
your own selectId implementation.,
The entity that was passed:, Object {
"undefined": Object {
"0": Object {
"__v": 5,
"_id": "6020b367cb94a91c9cd48c34",
"comments": Array [],
"date": "2021-02-08T03:43:35.742Z",
"likes": Array [
Object {
"_id": "60216bd341b3744ce4b13bee",
"user": "601f2d46017c85357800da96",
},
],
},
]

As this is written the postAdapter and the normalize are independent of each other. What's happening here is that normalize is attempting the map the data into entities, but it ends up with the key as undefined because it is using the default key of id which has an undefined value. Additionally you have an array of entities rather than just one.
The postAdapter then tries to find the selectId on this data which has already been normalized. But the postAdapter is not looking at a single post object -- it is looking at the nested data created by normalize. It is looking at the data structure Object { "undefined": Object { "0": Object {... from your error. The "undefined": is caused by the bad id attribute and the "0": is because it is an array.
How you do write this correctly kind of depends on what it is that you are trying to do. It's possible that normalize is not really necessary here. It looks like the likes in response.data are already returning a string id for the like and the user instead of the deeply-nested data that the normalizr package is designed to flatten. If you want to use normalize and have it work properly then you need to set the idAttribute on the options argument (docs link).
const postEntity = new schema.Entity('post', {}, {idAttribute: '_id'});
const normalized = data.map( o => normalize(o, postEntity));
idAttribute also accepts a function. You can create a reusable id extractor function that pulls the _id property from any object with {_id: string;}.
const myIdExtractor = (object : {_id: string;}) => object._id;
const postEntity = new schema.Entity('post', {}, {idAttribute: myIdExtractor});
const normalized = data.map( o => normalize(o, postEntity));
That same id extractor function also works with createEntityAdapter.
const postAdapter = createEntityAdapter({
selectId: myIdExtractor,
});

Related

Firebase Collection Stream to BigQuery Transform Function

the firebase extension "Stream Collections to BigQuery" allows for configuring a Transform Function for converting the Firestore Data Json to explicit BigQuery table fields.
https://firebase.google.com/products/extensions/firebase-firestore-bigquery-export
Can anyone point me to an example function or detailed docs for such functions?
Thanks, Ben
The transform Function should be an HTTP Cloud Function with the following logic (get the input object from the request, transform it, send it back in the response) as shown in the below CF skeleton:
exports.bqTransform = functions.https.onRequest(async (req, res) => {
const inputPayload = req.body // JS Object
// ...
// Transform the object
// ...
const outputPayload = {...} // JS Object
res.send(outputPayload);
});
As explained in the doc, the inputPayload object (i.e. req.body) contains a data property (which is an array) which contains a representation of the Firestore document, has shown below:
{
data: [{
insertId: int;
json: {
timestamp: int;
event_id: int;
document_name: string;
document_id: int;
operation: ChangeType;
data: string; // <= String containing the stringified object representing the Firestore document data
},
}]
}
The transformation implemented in your code shall create an object with the same structure (outputPayload in our skeleton example above) where the data[0].json property is adapted according to your transformation requirements.
Here is a very simple example in which we totally change the content of the Firestore record with some static data.
exports.bqTransform = functions.https.onRequest(async (req, res) => {
const inputPayload = req.body;
const inputData = inputPayload.data[0];
const outputPayload = [{
insertId: inputData.insertId,
json: {
timestamp: inputData.json.timestamp,
event_id: inputData.json.event_id,
document_name: inputData.json.document_name,
document_id: inputData.json.document_id,
operation: inputData.json.operation,
data: JSON.stringify({ createdOn: { _seconds: 1664983515, _nanoseconds: 745000000 }, array: ["a1", "a2"], name: "Transformed Name" })
},
}]
res.send({ data: outputPayload });
});

Redux Toolkit - assign entire array to state

How can I assign an entire array to my intialState object using RTK?
Doing state = payload or state = [...state, ...payload] doesn't update anything.
Example:
const slice = createSlice({
name: 'usersLikedPosts',
initialState: [],
reducers: {
getUsersLikedPosts: (state, { payload }) => {
if (payload.length > 0) {
state = payload
}
},
},
})
payload looks like this:
[
0: {
uid: '1',
title: 'testpost'
}
]
update
Doing this works but I don't know if this is a correct approach. Can anyone comment?
payload.forEach((item) => state.push(item))
immer can only observe modifications to the object that was initially passed into your function through the state argument. It is not possible to observe from outside the function if that variable was reassigned, as it only exists in the scope within the function.
You can, however, just return a new value instead of modifying the old one, if you like that better. (And in this case, it is probably a bit more performant than doing a bunch of .push calls)
So
return [...state, ...payload]
should do what you want.

Error when executing CosmosDB stored procedure

I am learning to write CosmosDB stored procedures following the following information
Stored procedure docs
What I am trying to do is loop through a number of documents returned by a query and find the one that has the most exact match.
The flow is as follows
Check Start and End Date to make sure its a valid documents
1.5 Check that the input VariantNo is included in the Variants array in the document
Check if a user is included in the user array of the document OR if ALL is specified as a string in the array
Check if a store is included in the stores array OR if ALL is specified as a string in the stores array
The document looks as follows
{
"id": "12345",
"brand": "XXX",
"PromotionName": "Test Promo 1",
"PromotionType": "Deal",
"PromotionSticker": "Sticker 1",
"StartDate": "2020-05-14T00:00:00.1212122Z",
"EndDate": "2020-05-30T00:00:00.1212122Z",
"Variants": [
"0628462008001",
"0628462008002",
"0644324003002"
],
"Stores": [
"SE0623"
],
"Users": [
"ALL"
],
"DiscountInPercent": "30",
"RedPriceStores": null,
"CreatedDate": "20200515",
"CreatedBy": "SLAPI Promotions API ClientId: 123",
"UpdatedDate": null,
"UpdatedBy": null,
"Consumer": "YYYYY_V2",
"_rid": "HwVmAIFaOoEBAAAAAAAAAA==",
"_self": "dbs/HwVmAA==/colls/HwVmAIFaOoE=/docs/HwVmAIFaOoEBAAAAAAAAAA==/",
"_etag": "\"11005859-0000-0c00-0000-5ebe0f7e0000\"",
"_attachments": "attachments/",
"_ts": 1589514110
}
The beginnings of my stored procedure looks like this based on the template in CosmosDB
// SAMPLE STORED PROCEDURE
function getFinalPromotionPrice(item, store, user) {
var collection = getContext().getCollection();
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM c WHERE c.StartDate <= (SELECT VALUE GetCurrentDateTime()) AND c.EndDate >= (SELECT VALUE GetCurrentDateTime())',
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found', 
// else take 1st element from feed
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var body = { prefix: prefix, feed: feed[0] };
response.setBody(JSON.stringify(body));
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
but I am getting this error when executing the stored procedure:
{"code":400,"body":{"code":"BadRequest","message":"Message: {\"Errors\":[\"Encountered exception while executing function. Exception = ReferenceError: 'prefix' is not defined\r\nStack trace: ReferenceError: 'prefix' is not defined\n at Anonymous function (script.js:20:13)\n at A
As you can check the error its expecting the "prefix" :
Exception = ReferenceError: 'prefix' is not defined
In the below line you are setting the value of prefix as "prefix" but you have not declared prefix anywhere in the code.
var body = { prefix: prefix, feed: feed[0] };
Change the above line to this if you don't require prefix in your SP body:
var body = { feed: feed[0] };

Issues after updating gremlin-server

Present i am using janusgraph-0.2.0-hadoop2 server and using gremlin#2.6.0 library for querying
const Gremlin = require("gremlin");
const client = Gremlin.createClient(8182, "192.168.0.103");
function test(p){
client.execute(q, {}, (err, results) => {
if (err) {
console.error(err);
client.closeConnection();
}
else {
console.log(results);
client.closeConnection();
}
});
}
for query g.V().count() result is [ 12 ]
for query g.V().has('name', 'saturn').valueMap() result is [ { name: [ 'saturn' ], age: [ 10000 ] } ]
I am ok with that
But after update my janusgraph to janusgraph-0.5.0-hadoop2 server and using same library gremlin#2.6.0
Getting data in different
for query g.V().count() result is [ { '#type': 'g:Int64', '#value': 12 } ]
for query g.V().has('name', 'saturn').valueMap() result is
[
{ '#type': 'g:Map', '#value': [ 'name', [Object], 'age', [Object] ] }
]
Updating library to gremlin#3.4.6
const gremlin = require('gremlin');
const client = new gremlin.driver.Client('ws://192.168.0.106:8182/gremlin', { traversalSource: 'g' });
async function test(q){
const res = await client.submit(q, {});
console.log('res',res)
client.close();
}
test()
for query g.V().count() result is [ 12 ]
for query g.V().has('name', 'saturn').valueMap() result is [ Map { 'name' => [ 'saturn' ], 'age' => [ 10000 ] } ]
Getting data in Hashmap
I want to know
1. Is it necessary to update gremlin library 3.4.6 getting correct result.
2. After updating to 3.4.6 get data in hashmap format, Means i want to know i am getting correct data or not.
3. I want data in object format but got in hashmap. I know i can convert to object but incase data is in nested hashmap, I dont want to repeat and convert it.
Please give me suggestions
I would say it is a very good idea to be on the current version of Janus Graph. Note that you should use the Gremlin libraries that come with Janus graph and not update those independently. The most recent Javascript/Node Gremlin clients do return Map types as you are seeing.

Redux - cross-entity state management

I'm using Redux and ImmutableJS to manage the state of my app. I've created the following two Records:
export const OrderRecord = Record({
id: null,
productId: null,
amount: 1,
});
export const ProductRecord = Record({
id: null,
name: '',
price: 0,
});
My global state is normalized based on the normalizr approach like this:
const state = {
entities: {
orders: new OrderedMap(new Map({
1: new OrderRecord(createOrderItem(1, 1)),
})),
products: new OrderedMap(new Map({
1: new ProductRecord(createProductItem(1)),
})),
},
};
I'm using this specification for testing purposes.
Now I'm trying to make some selects with computed fields using Reselect.
export const getVisibleOrders = createSelector(
[getProducts, getOrders],
(products, orders) => {
orders.map(order => {
const product = products.get(order.productId.toString());
if (!product) {
return order;
}
const totalPrice = order.amount * product.price;
order.set('productName', product.name);
order.set('totalPrice', totalPrice);
return order;
});
}
);
, but I get the following error message:
Error: Cannot set unknown key "productName" on Record
I know the reason - Record cannot contain any undefined keys, but my question is: Is there any suggested approach how gracefully solved this problem?
I don't want to extend my Records to support this kind of computed parameters (product.name and totalPrice).
I don't want to keep the static and computed parameters in one place, because for example the 'productName' parametr is from "Product" entity and not from "Order" entity.
Thank you.
The whole point of using Immutable.Record is to not let you add new keys to your record, hence the error message you get. And the whole point of selectors is to expose such "computed" property if you want to consume them outside. In your case, you can simply return a new Map() object or a new record type if you need to use the dotted syntax :
return Map({
productName: 'foobar'
}).merge(order)

Resources