mikro-orm Cascade.REMOVE, is it only for RDBMS? - mikro-orm

I am using mikro-orm with MongoDB and trying to do a Cascade.REMOVE but i can not get it work.
Business entity:
#Entity({ collection: "business" })
export class BusinessModel implements BusinessEntity {
#PrimaryKey()
public _id!: ObjectId;
#Property()
public name!: string;
#Property()
public description!: string;
#OneToMany({ entity: () => LocationModel, fk: "business", cascade: [Cascade.ALL] })
public locations: Collection<LocationModel> = new Collection(this);
}
export interface BusinessModel extends IEntity<string> { }
Location entity:
#Entity({ collection: "location" })
export class LocationModel implements LocationEntity {
#PrimaryKey()
public _id!: ObjectId;
#Property()
public geometry!: GeometryEmbedded;
#ManyToOne({ entity: () => BusinessModel})
public business!: BusinessModel;
public distance?: number;
}
export interface LocationModel extends IEntity<string> { }
Business Data:
_id: 5cac818273243d1439866227
name: "Prueba"
description: "Prueba eliminacion"
Location Data:
_id: 5cac9807179e380df8e43b6c
geometry: Object
business: 5cac818273243d1439866227
_id: 5cacc941c55fbb0854f86939
geometry: Object
business: 5cac818273243d1439866227
And the code:
export default class BusinessData {
private businessRepository: EntityRepository<BusinessModel>;
public constructor(#inject(OrmClient) ormClient: OrmClient) {
this.businessRepository = ormClient.em.getRepository(BusinessModel);
}
public async delete(id: string): Promise<number> {
return await this.businessRepository.remove(id);
}
}
The "business" is correctly removed but all related "locations" continue there.
The log only show:
[query-logger] db.getCollection("business").deleteMany() [took 0 ms]

Cascades work on application level, so for all drivers, including mongo.
The problem here is that you are removing the Business entity by id. You need to remove it by reference - provide the entity and be sure you have the collection populated, otherwise ORM does not know what entities to cascade remove.
Try it like this:
export default class BusinessData {
public async delete(id: string): Promise<number> {
const business = await this.businessRepository.findOne(id, ['locations'])
return await this.businessRepository.remove(business);
}
}
Your approach with removing by id would work only if the entity was already loaded in identity map (aka managed by EM), including the locations collection.

Related

TypeORM deletes record on entity change

I'm using NestJs + TypeORM with synchronise enabled.
I have 2 entities:
item
import { Entity } from 'typeorm';
import { BaseEntityUUID } from '../../common/entities/base-string.entity';
#Entity('items')
export class ItemEntity extends BaseEntityUUID {
}
and inventory
import { Column, Entity, ManyToOne } from 'typeorm';
import { BaseEntity } from '../../common/entities/base.entity';
import { ItemEntity } from '../../items/entities/item.entity';
import { GameUserEntity } from './game-user.entity';
#Entity('inventory-items')
export class InventoryItemEntity extends BaseEntity {
#Column()
gameUserId: string;
#Column()
itemId: string;
#ManyToOne(() => ItemEntity, {onUpdate: 'CASCADE', onDelete: 'CASCADE'})
item?: ItemEntity;
#Column()
count: number;
#ManyToOne(() => GameUserEntity, user => user.id, {onUpdate: 'CASCADE', onDelete: 'CASCADE'})
gameUser?: GameUserEntity;
}
I need to add new column to Item, so i modify ItemEntity like that:
import { Column, Entity } from 'typeorm';
import { BaseEntityUUID } from '../../common/entities/base-string.entity';
#Entity('items')
export class ItemEntity extends BaseEntityUUID {
#Column({ default: 0 })
levelRequired: number;
}
, but all records from Inventory are deleted after that. All records from Items are updated with levelRequired: 0, but why all related records from Inventory are erased?

In ionic how to read and display the information from firestore

In ionic, I want to get and display information from firestore from its specific fields like Name there, but the problem is that it is displaying other documents' field Names too.
ngOnInit(){
this.authService.readc().subscribe(data => {
this.datas = data.map(e => {
return {
Name: e.payload.doc.data()['Name'],
};
})
console.log(this.datas);
});
}
}
name() {
var counter = this.firestore.doc(`info/${this.authService.userDetails().uid}`);
counter.set({
Name: this.Name
})
}
In authService
readc() {
return this.firestore.collection('info').snapshotChanges();
}
Something like this maybe?
home.page.ts
export class HomePage {
names: any;
constructor(auth: AuthService) {
auth.readc().subscribe(data => {
this.names = data.map(person => ({ name: person.name }));
});
}
}
auth.service.ts
export class AuthService {
people: any;
constructor(db: AngularFirestore) {
this.people = db.collection('people').valueChanges();
}
readc(){
return this.people;
}
}
Check out the angularfire docs for more detailed information on using Collections and Documents in Firebase.
Hope this helps.

Angular 6 - a few components using the same randomised data that's called only once - how to avoid 'undefined'

I'm trying to use the same data in ngOnInit of several components, the problem is, the data are random every time I subscribe to it. I can only subscribe one time in the entire run of the app. That means, if I subscribe twice, it won't have any use because each component will have something else.
I want to subscribe once in a service and to create a global variable.
but when you try to use that global variable in all the components (on ngOnInit) it is always undefined.
How can I make the components wait for that same thing that can only be called once in the app?
export class UsersService {
public allUsers: Array<Object> = []
public allUsersUrl: string = 'https://glacial-escarpment-40412.herokuapp.com/users/'
constructor(private http: HttpClient) {
this.getAllUsers().subscribe(data => {
this.allUsers = data;
console.log(this.allUsers)
})
}
getAllUsers(): Observable<any> {
return this.http.get<any>(this.allUsersUrl);
}
getUser(id: number): Observable<any> {
return this.http.get<any>(this.allUsersUrl + id);
}
}
My components:
export class FirstComponent {
constructor(private usersService: UsersService){}
ngOnInit() {
console.log(this.usersService.allUsers) //undefined
}
export class SecondComponent {
constructor(private usersService: UsersService){}
ngOnInit() {
console.log(this.usersService.allUsers) //undefined
}
Please help me :)
You have a synchronism problem. This is your scenario
1- create first component
1.1 - injecting UsersService
1.2 - UsersService request for ASYNC data (execution continues)
2- FirstComponent get and print this.usersService.allUsers (not still populated because of async request)
3- this.usersService.allUsers is still undefined
You need Subject
Something like this:
UsersService
export class UsersService {
private _allUsersSource = new Subject<Array<Object>>();
private _allUsers$ = this._allUsersSource.asObservable();
public allUsersUrl: string = 'https://glacial-escarpment-40412.herokuapp.com/users/'
constructor(private http: HttpClient) {
this.getAllUsers();
}
getAllUsers(): Observable<any> {
return this.http.get<any>(this.allUsersUrl).subscribe(
data => this._allUsersSource.next(data)
);
}
get allUsers$(): Observable<Array<Object>> {
return this._allUsers$;
}
// OTHERS
}
FirstComponent
export class FirstComponent {
subscription: Subscription;
allUsers: Array<Object>;
constructor(private usersService: UsersService){}
ngOnInit() {
this.subscription = this.usersService.allUsers$.subscribe(users => {
this.allUsers = users;
console.log(this.allUsers);
});
}
Some thing for SecondComponent

Ngrx: combine two selectors

I've built this IStore:
export interface IStore {
user: IUser;
sources: ISourceRedux;
}
where IUser is:
export interface IUser {
id: string;
cname: string;
sname: string;
...
}
and ISourceRedux is:
export interface ISourceRedux {
entities: { [key: string]: ISource };
ids: Array<string>;
selectedIds: Array<string>;
editingSource: ISource;
defaultId: string;
}
So, I've created these selectors:
export const getSourcesState = (state: IStore) => state.sources;
export const getSelectedIds = (sourceRdx: ISourceRedux) => sourceRdx.selectedIds;
export const getSelectedSourceIds = createSelector(getSourcesState, fromSources.getSelectedIds);
So, up to now, in order to check if a user is logged I did that:
this.store$
.select(fromRoot.getUserState)
.filter(user => user.id != null && user.logged)
.do(user => this.store$.dispatch(...))
...
Now I'm strugling for getting user information and selectedSourceIds at the same time in order to check if:
a user is logged (this.store$.select(fromRoot.getUserState)
then get all selectedSourceIds (this.store.select(fromRoot.getSelectedSourceIds))
dispatch an action
How could I get this?
Would it make sense to add that code to a selector:
// Selector functions
const getProductFeatureState = createFeatureSelector<ProductState>('products');
const getUserFeatureState = createFeatureSelector<UserState>('users');
export const getCurrentProduct = createSelector(
getProductFeatureState,
getUserFeatureState,
getCurrentProductId,
(state, user, currentProductId) => {
if (currentProductId === 0) {
return {
id: 0,
productName: '',
productCode: 'New',
description: 'New product from user ' + user.currentUser,
starRating: 0
};
} else {
return currentProductId ? state.products.find(p => p.id === currentProductId) : null;
}
}
);
This code is in the product.reducer file. Here I define the feature selector both for the products and for the users.
I then build a getCurrentProduct selector using both the product and user feature.
This is my solution:
this.store$.combineLatest(
this.store$.select(fromRoot.getUserEntity),
this.store$.select(fromRoot.getSelectedSourceIds),
(store, user, selectedSourceIds) => ({user: user, selectedSourceIds: selectedSourceIds})
)
.filter((proj) => proj.user.id != null && proj.user.logged)
.do((proj) => this.store$.dispatch({type: 'DELETE_CARDS', payload: {username: proj.user.username, tokens: proj.selectedSourceIds}}))
.take(1)
.subscribe();
I hope it's useful.
I created a feature selector that combines two features to accomplish this.
The feature selector for the global module:
export interface ScaffoldPartialState extends GlobalPartialState {
readonly [SCAFFOLD_FEATURE_KEY]: State;
}
which I import to the Scaffold selectors and have ScaffoldPartialState extend it.
export interface ScaffoldPartialState extends GlobalPartialState {
readonly [SCAFFOLD_FEATURE_KEY]: State;
}
The createFeatureSelector only returns the state typed so that the returned type looks like it contains only the state for this feature.
The value is the complete application state, but the type makes it looks like it's only for one module.
By one type extending the other, the resulting type provides a property for both modules.
AppState
{
module1: { ... },
module2: { ... },
module3: { ... }
}
Module2PartialState
{
module2: { ... },
}
Module2PartialState extends Module3PartialState
{
module2: { ... },
module3: { ... }
}
This way the ScaffoldPartialState feature selector works for selectors of both modules.
Example:
export const getAuthorizedMenuItems = createSelector(
getScaffoldState,
GlobalSelectors.getUserPermissions,
getMenuItems,
(_globalState, userPermissions, menuItems) =>
userPermissions ? menuItems.filter(e => userPermissions.checkAllPermissions(e.permissions)) : []
);

Ionic2 load rows from sqlite after database ready

I have an ionic 2 app with a database service powered by sqlite. The database stores a couple rows of items. The first page of the app displays those items. The issue I'm running into is that the page attempts to load the items before the service has loaded the database. I get this error:
Cannot read property 'executeSql' of undefined
If i navigate to a different page and back to home, it loads correctly. This is the database service. I'm attempting to call getScanables on the first page.
import { Injectable } from '#angular/core';
import {SQLite} from 'ionic-native';
import {Platform} from 'ionic-angular';
#Injectable()
export class Database {
private storage: SQLite;
public isOpen: boolean;
public constructor(private platform: Platform,) {
console.log('Creating storage.');
platform.ready().then(() => {
console.log('Platform Ready.');
if(!this.isOpen) {
console.log('Database Unopened');
this.storage = new SQLite();
this.storage.openDatabase({name: "data.db", location: "default"}).then(() => {
console.log('Generating Database');
this.storage.executeSql("CREATE TABLE IF NOT EXISTS scanables (id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT, type TEXT, name TEXT, date TEXT)", []);
//this.storage.executeSql("CREATE TABLE IF NOT EXISTS journal (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT, descriptors TEXT, image TEXT, behaviors TEXT)", []);
console.log('Generated.');
this.isOpen = true;
}, (error) => {
console.log("Error opening database.", error);
});
}
});
}
public getScanables() {
console.log('Getting Scanables');
return new Promise((resolve, reject) => {
this.storage.executeSql("SELECT * FROM scanables", []).then((data) => {
let journal = [];
if(data.rows.length > 0) {
for(let i = 0; i < data.rows.length; i++) {
journal.push({
id: data.rows.item(i).id,
value: data.rows.item(i).value,
type: data.rows.item(i).type,
name: data.rows.item(i).name,
date: data.rows.item(i).date,
letter_one: data.rows.item(i).name.substring(0,1)
});
}
}
resolve(journal);
}, (error) => {
reject(error);
});
});
}
...
}
This is the relevant code for the first page:
#Component({
templateUrl: 'journal.html'
})
export class JournalPage implements AfterViewInit {
public orderType: string = 'delivery';
public autocomplete: {term: string, place: any};
public regions: any;
public itemList: Array<Object>;
public addList: Array<number>;
constructor(private navCtrl: NavController,
private menu: MenuController,
private modalCtrl: ModalController,
private database: Database,
public userService: UserService,
private helper: Helper,
private l: LoggerService) {
this.itemList = [];
this.addList = [1,2,3,4,5,6,7];
}
ngAfterViewInit(): any {
console.log('journal.ngAfterViewInit');
this.load();
}
public load() {
console.log('journal.load');
this.database.getScanables().then((result) => {
console.log('entered getScanables');
this.itemList = <Array<Object>> result;
console.log('itemList', this.itemList);
}, (error) => {
this.l.error('journal.ts:', error);
});
}
...
The solution for this issue was to make the service's database a public variable and load the database on load. Then only after loading the database we set the root page. This forces the database to load first. May not be the ideal solution though, so feel free to post additional answers.

Resources