Why can't I save the 'id' on MongoDB using Deno backend? - database

I am trying to save the id that I get from the MongoDB while saving the stock information as a property of stock using the following code but I get some errors:
async save(stock : Stock) {
const id = await stockCollection.insertOne(stock);
console.log('this is the id: ', id.toString());
stock.id = id.toString();
console.log(stock.id);
// delete stock._id;
return this;
}
The result of
console.log('this is the id: ', id.toString());
is:
this is the id: 621e826f90e8bf45a3fe493d
And the result of
console.log(stock.id);
Is also the same:
621e826f90e8bf45a3fe493d
But when I check the database I see the saved document like below:
{"_id":{"$oid":"621e7fc2f5b14f28463f289f"},"id":"","open":"2660.7250","high":"2660.7250","low":"2660.5700","close":"2660.5700","volume":"1826"}
It seems the line stock.id = id.toString(); doesn't work and it can not put the id into the id property of the stock.
Also when I try to remove the _id property of the saved stock using this line of the code:
delete stock._id;
It gives me this error:
Property '_id' does not exist on type 'Stock'.deno-ts(2339)
What is the problem and how can I resolve it?
EDIT: stock is an instance of Stock class that loads data of a stock using alpha vantage library.
stock.ts:
import { stockCollection } from "../mongo.ts";
import BaseModel from "./base_model.ts";
export default class Stock extends BaseModel {
public id: string = "";
public open: string = "";
public high: string = "";
public low: string = "";
public close: string = "";
public volume: string = "";
constructor({ id = "", open = "", high = "", low = "" ,close = "", volume = "" }) {
super();
this.id = id;
this.open = open;
this.high = high;
this.low = low;
this.close = close;
this.volume = volume;
}
static async findOne(params: object): Promise<Stock | null> {
const stock = await stockCollection.findOne(params);
if (!stock) {
console.log('there is no stock');
return null;
}
console.log('there is a stock');
return new Stock(Stock.prepare(stock));
}
async save(stock : Stock) {
const id = await stockCollection.insertOne(stock);
console.log('this is the id: ', id.toString());
stock.id = id.toString();
console.log(stock.id);
// delete stock._id;
return this;
}
}
BaseModel.ts:
export default class BaseModel {
public static prepare(data: any) {
data.id = data._id.toString();
// delete data._id;
return data;
}
}
Here I am trying to save one instance of a stock in database:
export const stockSave = async () => {
const YOUR_API_KEY = '***********';
const alpha = new AlaphaVantage('***********');
const writestock = await alpha.stocks.intraday('GOOG' , '1min' ).then((data: any) => {return data["Time Series (1min)"]["2022-02-28 14:31:00"]} );
console.log('this is writestock' , writestock);
const stock = new Stock({
id: "",
open: writestock["1. open"],
high: writestock["2. high"],
low: writestock["3. low"],
close: writestock["4. close"],
volume: writestock["5. volume"]});
await stock.save(stock);
}
Edit2: This is the whole project code: https://github.com/learner00000/back

Because you don't instantiate each instance of Stock with an actual ID, and you are instead relying on the ID to be generated by MongoDB, you should not store the id property in the database. You'll probably need to update your schema to reflect this.
You haven't shown a reproducible example with all of the imports and data, so I'm left to guess a bit about some types, but you can refactor the relevant parts of the class like this:
TS Playground
const stockDataKeys = ['close', 'high', 'low', 'open', 'volume'] as const;
type StockDataKey = typeof stockDataKeys[number];
type StockData = Record<StockDataKey, string>;
class Stock extends BaseModel implements StockData {
public id: string;
public close: string;
public high: string;
public low: string;
public open: string;
public volume: string;
constructor (init: Partial<StockData & { id: string; }> = {}) {
super();
this.id = init.id ?? '';
this.close = init.close ?? '';
this.high = init.high ?? '';
this.low = init.low ?? '';
this.open = init.open ?? '';
this.volume = init.volume ?? '';
}
getData (): StockData {
const data = {} as StockData;
for (const key of stockDataKeys) data[key] = this[key];
return data;
}
hasId (): boolean {
return Boolean(this.id);
}
async save (): Promise<void> {
const data = this.getData();
const id = await stockCollection.insertOne(data);
this.id = id.toString();
}
}

Related

Why is my filter for an array returning an empty list when there should be multiple matching items

My data is stored in two separate tables; "posts" and "profiles". Each User object comes from the "profiles" table but also has a list posts which is not a column in "profiles". Because of this, I need to fetch the posts first, then their corresponding users, then add each post to their User object based on "uid". My function below works for most of that but every user has an empty posts list, even when there should be posts.
const [posts, setPosts] = useState<Array<Post>>([]);
const [profiles, setProfiles] = useState<Array<User>>([]);
useEffect(() => {
async function fetchData() {
const { data: postsData } = await supabase.from("posts").select("*");
const postUids = postsData!.map((post) => post.uid);
const { data: profilesData } = await supabase
.from("profiles")
.select("*")
.in("uid", postUids);
setPosts(postsData!.map((post) => new Post(post)));
const profiles = profilesData!.map((userData: any) => {
const userPosts: Array<Post> = posts.filter(
(post) => post.uid === userData.uid
);
console.log("User posts: " + userPosts);
const user = new User({ ...userData, posts: userPosts });
// user.posts = [...user.posts, ...userPosts];
console.log(user);
return user;
});
setProfiles((prevUsers) => [...prevUsers, ...profiles]);
console.log(profiles);
}
fetchData();
}, []);
console.log(posts);
console.log(profiles);
Example of postsData:
[{
"caption":"Caption",
"date":"1669244422569",
"imageUrls":[
"https://cdn.pixabay.com/photo/2020/05/04/16/05/mckenzie-river-5129717__480.jpg"
],
"location":{
"latitude":150,
"locationInfo":"City, State",
"longitude":-150
},
"postId":"1669244407166",
"uid":"daf6b8be-7cd0-4341-89d7-07879b207087"
}]
Post object:
export default class Post {
imageUrls: Array<string>;
postId: string;
uid: string;
caption: string;
location: Location;
date: number;
constructor(post: any) {
this.imageUrls = post.imageUrls;
this.postId = post.postId;
this.uid = post.uid;
this.caption = post.caption;
this.location = post.location;
this.date = post.date;
}
}
Example of profilesData:
{
"blockedUsers":[],
"displayName":"name",
"photoURL":"https://cdn.pixabay.com/photo/2020/05/04/16/05/mckenzie-river-5129717__480.jpg",
"uid":"daf6b8be-7cd0-4341-89d7-07879b207087",
"verified":false
}
User object:
export default class User {
uid: string;
blockedUsers: Array<string>;
posts: Array<Post>;
photoURL: string;
displayName: string;
verified: boolean;
constructor(user: any) {
this.uid = user.uid;
this.blockedUsers = user.blockedUsers;
this.posts = user.posts;
this.photoURL = user.photoURL;
this.displayName = user.displayName;
this.verified = user.verified;
}
}
Not entirely sure why you are not getting any posts data, might be due to how your RLS is configured, but there is a better way to query your data.
You can query posts and profiles at the same time like this:
const { data, error } = await supabase.from("profiles").select("*, posts(*)");
This way, you don't have to do another query to retrieve the profiles separately, and you also don't have to loop through the retrieved objects to modify them.

Method must be called after React Context render

I've encountered an issue while using React Context. I use something like this to get the selected answer from the context:
const [selectedAnswer, setSelectedAnswer] = React.useState<boolean>(
isSelectedAnswer()
);
function isSelectedAnswer(): boolean {
return QuestionContext!
.getAnswers()
[QuestionContext!.getQuestionIndex(heading)].find(
({ name, value }) => name === heading && value === true
)
? true
: false;
}
But I receive a strange error:
TypeError: this.state.questions.find(...) is undefined
While typeof this.state.questions.find is function, obviously.
I believe the issue is happening, because the QuestionContext's React Component, and in the useState function it's not ready declared. I've used Profiler function, with onRender callback, but I realised that the Profiler function won't be a good candidate for that type of error. But yeah, it worked fine. Anyone knows how to fix that issue?
EDIT: Here's the Context code, which is returning error with the state.
import React, { Component } from "react";
import {
Question,
Questions,
Answers,
Answer,
ParsedAnswer,
Preset,
Presets,
} from "../../typings";
import QuestionsContext from "../QuestionsContext";
import questions from "../../data/questions";
import presets from "../../data/presets";
type QuestionsContextProviderProps = {
children: any;
};
export type QuestionsContextProvidedState = {
questions: Questions;
presets: Presets;
categories: string[];
answers: Answers;
selectedPreset: Preset | null;
getPresets: () => Presets;
getPresetForQuestion: (
questionCategoryIndex: number,
questionIndex: number
) => string;
setPreset: (preset: Preset) => void;
setPresetsInAnswers: (preset: Preset) => void;
getSelectedPreset: () => Preset | null;
getQuestions: () => Questions;
getFieldNameCategory: (fieldName: string) => string;
getQuestionIndex: (fieldName: string) => number;
getQuestionsFromIndex: (index: number) => Question[];
getAnswers: () => Answers;
sendNewAnswer: (
questionIndex: number,
fieldName: string,
answer: string | boolean
) => void | boolean | string;
parseAnswers: () => string;
};
export default class QuestionsContextProvider extends Component<
QuestionsContextProviderProps,
QuestionsContextProvidedState
> {
public readonly state: QuestionsContextProvidedState = {
questions: questions,
presets: presets,
categories: [],
answers: [],
selectedPreset: null,
getPresets: this.getPresets.bind(this),
getPresetForQuestion: this.getPresetForQuestion.bind(this),
setPreset: this.setPreset.bind(this),
setPresetsInAnswers: this.setPresetsInAnswers.bind(this),
getSelectedPreset: this.getSelectedPreset.bind(this),
getQuestions: this.getQuestions.bind(this),
getFieldNameCategory: this.getFieldNameCategory.bind(this),
getQuestionIndex: this.getQuestionIndex.bind(this),
getQuestionsFromIndex: this.getQuestionsFromIndex.bind(this),
getAnswers: this.getAnswers.bind(this),
sendNewAnswer: this.sendNewAnswer.bind(this),
parseAnswers: this.parseAnswers.bind(this),
};
constructor(props: any) {
super(props);
const endingAnswersArray = this.state.answers;
const endingCatagoriesArray = this.state.categories;
const alreadyUsedKeys: string[] = [];
this.state.questions.forEach(({ key }) => {
if (alreadyUsedKeys.find((value) => value === key)) return;
endingAnswersArray.push([]);
endingCatagoriesArray.push(key);
alreadyUsedKeys.push(key);
});
this.state.answers = endingAnswersArray;
this.state.categories = endingCatagoriesArray;
}
public getPresets(): Presets {
return this.state.presets;
}
public getPresetForQuestion(
questionCategoryIndex: number,
questionIndex: number,
preset?: Preset
): string {
return this.state.questions[questionCategoryIndex].values[questionIndex]
.presets[preset || this.state.selectedPreset!];
}
public setPreset(preset: Preset): void {
this.setState({
selectedPreset: preset,
});
}
private getDefaultValueForQuestion(
questionCategoryIndex: number,
questionIndex: number
): string {
return this.state.questions[questionCategoryIndex].values[questionIndex]
.defaultValue;
}
public setPresetsInAnswers(preset: Preset): void {
this.state.categories.forEach((_, key) => {
this.state.questions.forEach(({ values }, idx) => {
values.forEach(({ name }, questionKey) => {
const selectedPreset = this.getPresetForQuestion(
idx,
questionKey,
preset
);
let newPreset: string | boolean = selectedPreset;
if (selectedPreset === "true") newPreset = true;
if (selectedPreset === "false") newPreset = false;
if (
selectedPreset === this.getDefaultValueForQuestion(idx, questionKey)
)
return;
this.sendNewAnswer(key, name, newPreset);
});
});
});
}
public getSelectedPreset(): Preset | null {
return this.state.selectedPreset;
}
public getQuestions(): Questions {
return this.state.questions;
}
public getFieldNameCategory(fieldName: string): string {
return this.state.questions.find(({ values }) =>
values.find(({ name }) => name === fieldName)
)!.key;
}
public getQuestionIndex(fieldName: string): number {
return this.state.categories.findIndex(
(value) => value === this.getFieldNameCategory(fieldName)
);
}
public getQuestionsFromIndex(index: number): Question[] {
return this.state.questions[index].values;
}
public getAnswers(): Answers {
return this.state.answers;
}
public sendNewAnswer(
questionIndex: number,
fieldName: string,
answer: string | boolean
): void | boolean | string {
const alreadyAnsweredField = this.state.answers[questionIndex].find(
({ name }) => name === fieldName
);
if (alreadyAnsweredField) {
return (alreadyAnsweredField.value = answer);
}
const endingAnswersField = this.state.answers;
endingAnswersField[questionIndex].push({
name: fieldName,
value: answer,
});
this.setState({
answers: endingAnswersField,
});
}
public parseAnswers(): string {
const parsedAnswer: ParsedAnswer = {};
this.state.categories.forEach((value, key) => {
parsedAnswer[value] = {};
this.getAnswers()[key].forEach((answer: Answer) => {
parsedAnswer[value][answer.name] = answer.value;
});
});
return JSON.stringify(parsedAnswer, null, 2);
}
public render() {
return (
<QuestionsContext.Provider value={this.state}>
{this.props.children}
</QuestionsContext.Provider>
);
}
}

Web3.utils.hexToUtf8 getting "undefined" error

I am trying to return an username from a struct inside a mapping and I am getting this error:
Error: The parameter "undefined" must be a valid HEX string.
contract UserStorage is BaseStorage {
event UserCreated(string _message);
struct Profile {
uint userId;
bytes32 username;
}
mapping(address => Profile) public profiles;
mapping(address => bool) public addresses;
mapping (bytes32 => uint) public usernames;
uint latestUserId = 0;
function createUser(
address _address,
bytes32 _username
) public onlyControllerAddr returns(uint) {
latestUserId++;
profiles[_address] = Profile(
latestUserId,
_username
);
addresses[_address] = true;
usernames[_username] = latestUserId;
emit UserCreated("Membership Confirmed");
return latestUserId;
}
}
class UserAccount extends React.Component {
state = { dataKey: null };
componentDidMount() {
const { drizzle, drizzleState } = this.props;
const contract = drizzle.contracts.UserStorage;
const account = drizzleState.accounts[0];
const dataKey = contract.methods['profiles'].cacheCall(account);
this.setState({ dataKey });
}
render() {
const { UserStorage } = this.props.drizzleState.contracts;
const displayData = UserStorage.profiles[this.state.dataKey];
const user = displayData && displayData.value[1];
const username = Web3.utils.hexToUtf8(user);
console.log(user);
return (
<div>Hello: {user}</div>
)
}
}
export default UserAccount;
Here are values that are returned:
user:
0x426f620000000000000000000000000000000000000000000000000000000000
const displayData:
{args: Arguments(1), fnIndex: 6, value: Result, error: null}args: Arguments ["0x1C16e11e45Ae1601186E05EE64f38276FAb2239a", callee: (...), Symbol(Symbol.iterator): ƒ]error: nullfnIndex: 6value: Result {0: "1", 1: "0x426f620000000000000000000000000000000000000000000000000000000000", userId: "1", username: "0x426f620000000000000000000000000000000000000000000000000000000000"}__proto__: Object

Why is my Many to Many relationship field undefined?

I am trying to create a follower/following system and when I try to append the new user to the following list I get the error Cannot read property 'push' of undefined. This ends up creating 2 separate tables one for users following other users and one for users being followed by other users. Not sure why it's not picking up the field? Any help is appreciated.
import { Length } from "class-validator";
import {
Column,
CreateDateColumn,
Entity,
JoinTable,
ManyToMany,
OneToMany,
PrimaryColumn,
RelationCount,
Unique,
UpdateDateColumn
} from "typeorm";
export class User {
#PrimaryColumn()
public user_id: string;
#Column()
public first_name: string;
#Column()
public last_name: string;
#Column()
public email: string;
#Column()
public phone_number: string;
#Column()
public username: string;
#Column()
#CreateDateColumn()
public created_on: Date;
#Column()
#UpdateDateColumn()
public updated_at: Date;
#ManyToMany((type) => User, (user) => user.following)
#JoinTable()
public followers: User[];
#ManyToMany((type) => User, (user) => user.followers)
#JoinTable()
public following: User[];
#RelationCount((user: User) => user.followers)
public followers_count: number;
#RelationCount((user: User) => user.following)
public following_count: number;
}
const { user_id,
follow_user_id } = req.
const user_repo = getRepository(User);
const user = await user_repo.findOne({
where: {user_id}
});
const follow_user = new User();
follow_user.user_id = follow_user_id;
user.following.push(follow_user);
const result = user_repo.save(user);
Error is referring to this line user.following.push(follow_user);
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'push' of undefined
I encountered a similar error with OneToMany and ManyToOne relations where the relative returned null/undefined.
The workaround I'm using involves putting this in the User class:
#AfterLoad()
async nullChecks() {
if (!this.followers) {
this.followers = []
}
if (!this.following) {
this.following = []
}
}
documentation
I didn't test ways below, but think one of them should help you.
1st way. At your User class.
// Source code omission
#ManyToMany((type) => User, (user) => user.followers)
#JoinTable()
public following: User[] = []; // ★ Added assign
// Source code omission
2nd way. At your User class.
export class User {
// Source code omission
constructor() { // ★ Added line
this.following = []; // ★ Added line
} // ★ Added line
}
3rd way. At place where you use User class.
const follow_user = new User();
follow_user.user_id = follow_user_id;
user.following = []; // ★ Added line
user.following.push(follow_user);
const result = user_repo.save(user);
4th way. At place where you use User class.
const follow_user = new User();
follow_user.user_id = follow_user_id;
user.following = [follow_user]; // ★ Edited line
const result = user_repo.save(user);
We use this approach to avoid undefined lists:
#ManyToMany((type) => User, (user) => user.following)
#JoinTable()
private _followers: User[];
...
get followers() : User[] {
if (!_followers) {
_followers = [];
}
return _followers;
}

Trouble with Upload Task Snapshot

I have a problem with displaying image on my project. I want in the .subscribe task the download url to equal as image. So it can display it. But when I do that it shows an error message:
Type 'UploadTaskSnapshot' is not assignable to type 'string'.
Here is the code:
export class PostDashboardComponent implements OnInit {
title: string;
image: string = null;
content: string;
buttonText: string = "Create Post";
uploadPercent: Observable<number>;
downloadURL: Observable<string>;
constructor(
private auth: AuthService,
private postService: PostService,
private storage: AngularFireStorage
) { }
ngOnInit() {
}
uploadImage(event) {
const file = event.target.files[0]
const path = `posts/${file.name}`
const fileRef = this.storage.ref(path);
if (file.type.split('/')[0] !== 'image') {
return alert('only image files')
} else {
const task = this.storage.upload(path, file)
this.uploadPercent = task.percentageChanges();
task.snapshotChanges().pipe(
finalize(() => this.downloadURL = fileRef.getDownloadURL() )
)
.subscribe(url => (this.image = url))
console.log('Image Uploaded!');
What should I change to make it work because I am an amateur. Thank you for your help.
I found the answer and is this:
uploadImage(event) {
const file = event.target.files[0]
const path = `posts/${file.name}`
const fileRef = this.storage.ref(path);
if (file.type.split('/')[0] !== 'image') {
return alert('only image files')
} else {
const task = this.storage.upload(path, file);
const ref = this.storage.ref(path);
this.uploadPercent = task.percentageChanges();
console.log('Image uploaded!');
task.snapshotChanges().pipe(
finalize(() => {
this.downloadURL = ref.getDownloadURL()
this.downloadURL.subscribe(url => (this.image = url));
})
)
.subscribe();

Resources