Folks,
How does one access the parent entities? Lets imagine I have 2 Kinds, users and say cars... When Kind cars is queried by plate, it returns all the entities that match the plate. How can one also get those entities' ancestor information? What if I wanted to get the parent key for the returned' entities? Is this possible?
Example where I only am able to retrieve the entities
const query = datastoreClient.createQuery('cars')
.filter('plate', '=', 'ABC');
return datastoreClient.runQuery(query).then(function (entities) {
console.log(entities);
return entities;
});
output:
[
[
{
"id": "2b49ca40-a5fb-11e7-ad5c-c95aa76161c6",
"plate": "ABC"
}
],
{
"moreResults": "NO_MORE_RESULTS",
"endCursor": "Cn0Sd2oWc35za2lsZnVsLWZyYW1lLTE4MDIxN3JdCxIFdXNlcnMiJDIxMmU2MTQwLWE1OTQtMTFlNy1iMGQ1LWUxNjBjMjA0NjI2MQwLEgRjYXJzIiQyYjQ5Y2E0MC1hNWZiLTExZTctYWQ1Yy1jOTVhYTc2MTYxYzYMGAAgAA=="
}
]
Here you have the Node.js code that does what you want:
const Datastore = require('#google-cloud/datastore');
const projectId = 'some-projectid'
// Creates a client
const datastore = new Datastore({
projectId: projectId,
});
const Query = datastore
.createQuery('cars')
.filter('plate', '=', 'some-plate-number')
datastore.runQuery(Query).then(results => {
// Task entities found.
const tasks = results[0];
console.log('Tasks:');
tasks.forEach(task => console.log(task));
Output:
Tasks: { plate: 'some-plate-number', [Symbol(KEY)]: Key {
namespace: undefined,
id: 'id-of-the-child-entity',
kind: 'cars',
parent:
Key {
namespace: undefined,
id: 'id-of-the-parent-entity',
kind: 'users',
path: [Getter] },
path: [Getter] } }
Related
I'm using strapi 4 with nextjs.
In the app strapi holds music events for each user and each user should be able add and retrieve there own music events.
I am having trouble retrieving
each users music events from strapi 4
I have a custom route and custom controller
The custom route is in a file called custom-event.js and works ok it is as follows:
module.exports = {
routes: [
{
method: 'GET',
path: '/events/me',
handler: 'custom-controller.me',
config: {
me: {
auth: true,
policies: [],
middlewares: [],
}
}
},
],
}
The controller id a file called custom-controller.js and is as follows:
module.exports = createCoreController(modelUid, ({strapi }) => ({
async me(ctx) {
try {
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [
{messages: [{ id: 'No authorization header was found'}]}
])
}
// The line below works ok
console.log('user', user);
// The problem seems to be the line below
const data = await strapi.services.events.find({ user: user.id})
// This line does not show at all
console.log('data', data);
if (!data) {
return ctx.notFound()
}
return sanitizeEntity(data, { model: strapi.models.events })
} catch(err) {
ctx.body = err
}
}
}))
Note there are two console.logs the first console.log works it outputs the user info
The second console.log outputs the data it does not show at all. The result I get back
using insomnia is a 200 status and an empty object {}
The following line in the custom-controller.js seems to be where the problem lies it works for strapi 3 but does not seem to work for strapi 4
const data = await strapi.services.events.find({ user: user.id})
After struggling for long time, days infact, I eventually got it working. Below is the code I came up with. I found I needed two queries to the database, because I could not get the events to populate the images with one query. So I got the event ids and then used the event ids in a events query to get the events and images.
Heres the code below:
const utils = require('#strapi/utils')
const { sanitize } = utils
const { createCoreController } = require("#strapi/strapi").factories;
const modelUid = "api::event.event"
module.exports = createCoreController(modelUid, ({strapi }) => ({
async me(ctx) {
try {
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [
{messages: [{ id: 'No authorization header was found'}]}
])
}
// Get event ids
const events = await strapi
.db
.query('plugin::users-permissions.user')
.findMany({
where: {
id: user.id
},
populate: {
events: { select: 'id'}
}
})
if (!events) {
return ctx.notFound()
}
// Get the events into a format for the query
const newEvents = events[0].events.map(evt => ({ id: { $eq: evt.id}}))
// use the newly formatted newEvents in a query to get the users
// events and images
const eventsAndMedia = await strapi.db.query(modelUid).findMany({
where: {
$or: newEvents
},
populate: {image: true}
})
return sanitize.contentAPI.output(eventsAndMedia,
strapi.getModel(modelUid))
} catch(err) {
return ctx.internalServerError(err.message)
}
}
}))
I am doing a simple command to list all the items in my table. However, the data I am getting back is not structured the way I want. I want a simple JSON structure but DynamoDB is turning the results into nested objects.
DynamoDB gives me below response:
// What I am currently getting
[
{
id: { S: '8' },
lastName: { S: 'Perry' },
firstName: { S: 'Matthew' }
},
{
id: { S: '3' },
firstName: { S: 'Joan' },
lastName: { S: 'Peter' }
}
]
But I want this:
// What I want
[
{
id: 8
lastName: 'Perry' ,
firstName: 'Matthew'
},
{
id: 3,
firstName: 'Joan' ,
lastName: 'Peter'
}
]
How can I achieve the later result set. Below is my code:
const { ExecuteStatementCommand } = require('#aws-sdk/client-dynamodb')
const { ddbDocClient, memberTableName } = require('./client.js')
const selectAll = async () => {
const params = {
Statement: `SELECT * FROM ${memberTableName}`,
Parameters: [{ S: '3' }]
}
console.log(params)
return await ddbDocClient.send(new ExecuteStatementCommand(params));
}
selectAll()
.then(d => console.log(d.Items))
ddbDocClient was created like this:
const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
The command import is incorrect. To send and receive native JS types, import the ExecuteStatementCommand command from the #aws-sdk/lib-dynamodb "document client" package.
const { ExecuteStatementCommand } = require('#aws-sdk/lib-dynamodb');
You are importing the command from the "regular client" package #aws-sdk/client-dynamodb, i.e. the one that accepts and returns DynamoDB JSON.
Note: The Parameters: [{ S: '3' }] line is also wrong, but it's currently not causing trouble because your statement is scanning for all records. If you were to include a WHERE id=? phrase in the statement, make sure to change the parameters to Parameters: ['3']. You must pass JS types to the "document client" commands.
You need use simple lib https://www.npmjs.com/package/#aws-sdk/util-dynamodb
then use like this:
const { DynamoDB } = require("#aws-sdk/client-dynamodb");
const { marshall, unmarshall } = require("#aws-sdk/util-dynamodb");
const client = new DynamoDB(clientParams);
const params = {
TableName: "Table",
Key: marshall({
HashKey: "hashKey",
}),
};
const { Item } = await client.getItem(params);
unmarshall(Item);
I need to make a referene with different entities within one reference. Is it possible to do this with MikroORM ?
Example of what I need:
export const userSchema = new EntitySchema<any>({
class: userEntity,
tableName: 'users',
properties: {
uuid: { type: 'uuid', primary: true },
bills: {
reference: '1:m',
entity: () => BillsFromBank || BillsFromAnotherBank,
mappedBy: (a) => a._user,
},
},
});
Is it possible to create some conditions for such a connection 1 reference field with different entities?
In a react-native project using Realm-js, I've just created a clone of the app, integrated all libs, and copied over all src directories.
The app builds installs and runs on Android.
When i go through the authentication flow (which utilizes realm to store auth data), i ultimately get an error:
[ Error: RealmObject cannot be called as a function ]
login function:
async function login(username, password) {
try {
const result = await Api.login({
username: username,
pass: password,
});
const userAuthResult = await Db.updateAuth(result);
setUserAuth(userAuthResult);
} catch (err) {
console.log('[ ERROR ]:', err)
if (!err.message || err.message.includes('Network Error')) {
throw new Error('Connection error');
}
throw new Error('Wrong username or password');
}
}
and ive narrowed down the issue to Db.updateAuth(...)
updateAuth:
export const updateAuth = (params) => {
console.log(' [ HERE 1 ]')
const auth = {
id: params.id,
token: params.token,
refreshToken: params.refresh_token,
tokenExpiresAt: Math.floor(Date.now() / 1000) + 600, //params.expires_at,
federatedToken: params.federatedToken ?? '',
federatedTokenExpiresAt: params.federatedTokenExpiresAt ?? 0,
username: params.username,
name: params.name,
roleName: params.role_name,
roleId: params.role_id,
lastLogin: Math.floor(Date.now() / 1000),
};
console.log(' [ HERE 2 ]')
realm.write(() => {
console.log(' [ HERE 3 ]')
realm.create('Authorizations', auth, 'modified'); // PROBLEM
});
return auth;
};
inspecting the schema, i found theres no federatedToken propereties, yet in the auth update object, there are two. not sure why it wouldnt be throwing an error in the original non-cloned app.
authorizations schema:
AuthorizationsSchema.schema = {
name: 'Authorizations',
primaryKey: 'id',
properties: {
id: 'int',
token: 'string',
refreshToken: 'string',
tokenExpiresAt: 'int',
username: 'string',
name: 'string',
roleName: 'string',
roleId: 'int',
lastLogin: 'int',
},
};
Realm.js (class declaration) -> https://pastebin.pl/view/c903b2e2
from realm instantiation:
let realm = new Realm({
schema: [
schema.AccountSchema,
schema.AuthorizationsSchema,
schema.AvailableServiceSchema,
schema.FederatedTokensSchema,
schema.NoteSchema,
schema.PhotoSchema,
schema.PhotoUploadSchema,
schema.PrintQueueSchema,
schema.ProductSchema,
schema.ReportSchema,
schema.ServicesSchema,
schema.UploadQueueJobSchema,
schema.InvoicesSchema,
schema.TestSchema
],
schemaVersion: 60,
deleteRealmIfMigrationNeeded: true,
//path: './myrealm/data',
});
this logs the 1, 2, and 3 statements. The issue seems to come from the 'problem' line. Im not sure what exactly this error means, as there doesnt seem to be anything in realm's repo about it, and in the app this was cloned from, there was no issue with this line. I can also see other lines are throwing similar errors later on the user flows
Anyone know what this is about? or where i can learn more?
React-native: v64.2
realm-js: 10.6.0 (app cloned from was v10.2.0)
MacOS: 11.3 (M1 architecture)
in order to create you have the first call, the realm.write a method like this.
const storeInDataBase = (res,selectedfile) => {
try{
realm.write(() => {
var ID =
realm.objects(DocumentConverstionHistory).sorted('HistoryID', true).length > 0
? realm.objects(DocumentConverstionHistory).sorted('HistoryID', true)[0]
.HistoryID + 1
: 1;
realm.create(DocumentConverstionHistory, {
HistoryID: ID,
Name:`${selectedfile.displayname}.pdf`,
Uri:`file://${res.path()}`,
Date: `${new Date()}`
});
})
}catch(err){
alert(err.message)
}
}
Here is the schema file
export const DATABASENAME = 'documentconverter.realm';
export const DocumentConverstionHistory = "DocumentConverstionHistory"
export const DocumentConverstionHistorySchema = {
name: "DocumentConverstionHistory",
primaryKey: 'HistoryID',
properties: {
HistoryID: {type: 'int'},
Name: {type: 'string'},
Uri: {type: 'string?'},
Type: {type: 'string?'},
Size: {type: 'string?'},
Date: {type: 'date?'}
}
};
I have a deeply nested data object that comes back from my API which looks like the JSON below.
I am using Redux toolkit's createSlice to create a slice of a trip
So currently in my createSlice, I want to store an array of trips.
I also want the ability to update a single trip or part of the trip
for example, let's say I want to update a trip item's start date
or, let's say I want to update a trip item's member's name
My questions and concerns:
I currently have all of these entities coming back into the trip createSlice but I am not sure if, once the entities are normalized, should they be separated into separate createSlices? if so, how is this done or is this an anti pattern?
how should nested entities be defined in initialState?
should I define all of my normalized entities in my initalState?
if I do that, how would my reducers look like when I want to update a trip_item or trip_item_member ?
does my normalized data even look "correct"? I have omitted using mergeStrategy between trips_items_members and trip_members which I know I should do but haven't figured out how that works yet or if it's necessary here?
Note:
There is an example in the RTK docs here which shows createSlice being used with 3 separate entities, which originally came from 1 API call. It looks like 3 separate files however it is unclear how data is shared amongst them.
This is how my trip createSlice looks like
/**
* Get trip by ID action
*/
export const getTripByID = createAsyncThunk(
'trips/getTripByID',
async ({ uid }) => {
const response = await findOne(uid)
const normalized = normalize(response, trip)
return normalized.entities
},
)
const tripsAdapter = createEntityAdapter({
selectId: entity => entity.trip_id,
sortComparer: (a, b) => b.start_date.localeCompare(a.start_date),
loading: '',
error: '',
data: [],
})
export const {
selectById: selectTripById,
selectIds: selectTripIds,
selectEntities: selectTripEntities,
selectAll: selectAllTrips,
selectTotal: selectTotalTrips,
} = tripsAdapter.getSelectors(state => state.trip)
const initialState = tripsAdapter.getInitialState()
const tripSlice = createSlice({
name: 'trips',
initialState,
extraReducers: builder => {
builder.addCase(getAllTrips.fulfilled, (state, { payload }) => {
tripsAdapter.upsertMany(state, payload)
state.loading = false
})
builder.addCase(getTripByID.fulfilled, (state, { payload }) => {
console.log('payload', payload)
tripsAdapter.upsertMany(state, payload)
state.loading = false
})
},
})
export default tripSlice.reducer
API response that comes back from await findOne(uid)
{
created_by: "6040c2d1-ea57-43b6-b5f2-58e84b220f4e",
deleted_by: null,
destination: "Valencia",
end_date: "2020-10-04",
start_date: "2020-09-27",
trip_id: "34a620e8-51ff-4572-b466-a950a8ce1c8a",
uid: "14047a5b-2fe5-46c9-b7f2-e9b5d14db05b",
updated_by: null,
trip_items: [
{
destination: "Mezzanine Level Shivaji Stadium Metro Station, Baba Kharak Singh Rd, Hanuman Road Area, Connaught Place, New Delhi, Delhi 110001, India",
end_date: "2020-09-28",
end_time: "2020-09-28T01:20:15.906Z",
note: null,
start_date: "2020-09-28",
start_time: "2020-09-28T01:20:15.906Z",
trip_item_id: "bd775be7-2129-42c0-a231-5a568b0f565d",
trips_items_members: [
{
trip_item_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc",
uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02",
}
],
uid: "e5f81a6d-1a0d-4456-9d4e-579e80bc27d8",
}
],
trips_members: [
{
trip_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc",
uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02",
role: "ADMIN"
}
]
}
This is my normalizr schema
const tripItemMember = new schema.Entity(
'trips_items_members',
{},
{ idAttribute: 'trip_item_member_id' },
)
const tripItem = new schema.Entity(
'trips_items',
{
trips_items_members: [tripItemMember],
},
{
idAttribute: 'trip_item_id',
},
)
const tripMember = new schema.Entity(
'trips_members',
{},
{
idAttribute: 'trip_member_id',
},
)
export const trip = new schema.Entity(
'trip',
{
trips_items: [tripItem],
trips_members: [tripMember],
},
{
idAttribute: 'trip_id',
},
)
This is the output from normalizr
trip: {
"34a620e8-51ff-4572-b466-a950a8ce1c8a": {
created_by: "6040c2d1-ea57-43b6-b5f2-58e84b220f4e"
deleted_by: null
destination: "Valencia"
end_date: "2020-10-04"
start_date: "2020-09-27"
trip_id: "34a620e8-51ff-4572-b466-a950a8ce1c8a"
trips_items: ["bd775be7-2129-42c0-a231-5a568b0f565d"]
trips_members: ["76b54a80-4d09-4768-bc5a-4d7e153e66dc"]
uid: "14047a5b-2fe5-46c9-b7f2-e9b5d14db05b"
updated_by: null
}
}
trips_items:{
"0a56da0f-f13b-4c3d-896d-30bccbe48a5a": {
destination: "Mezzanine Level Shivaji Stadium Metro Station"
end_date: "2020-09-28"
end_time: "2020-09-28T01:20:15.906Z"
note: null
start_date: "2020-09-28"
start_time: "2020-09-28T01:20:15.906Z"
trip_item_id: "0a56da0f-f13b-4c3d-896d-30bccbe48a5a"
trips_items_members: []
uid: "25d20a9d-1eb9-4226-926d-4d743aa9d5dc"
}
}
trips_members: {
"76b54a80-4d09-4768-bc5a-4d7e153e66dc": {
role: "ADMIN"
trip_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc"
uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02"
}
}
Your setup is very much like this detailed example from the redux-toolkit docs. They are fetching articles, but each article comes with embedded users and comments. They define separate slices for each of the three entities.
The comments slice has no actions or reducers of its own, but it uses the extraReducers property to respond to the article received action and store the embedded comments.
const commentsAdapter = createEntityAdapter();
export const slice = createSlice({
name: "comments",
initialState: commentsAdapter.getInitialState(),
reducers: {},
extraReducers: {
[fetchArticle.fulfilled]: (state, action) => {
commentsAdapter.upsertMany(state, action.payload.comments);
}
}
});
The fetchArticle action is "owned" by the article slice, but the action payload contains entities from all three types. All slices receive all actions, so the comments and users are able to respond to this action with their own logic. Each slice doesn't have any effect on what the others can or can't do.
In your case you want to create slices for items and members. Instead of calling upsertMany(state, payload), you want the payload to be keyed by entity type so that you can call upsertMany(state, payload.members).