I've been trying to recover data querying with an include, my first model is "Block" and this has a "hasMany" relation to the destiny model called "Site". Well, usually this works as intended but this time it has a catch, the data is stored in mongodb with ObjectID(), so if I try to query it on robo3t or whereever, I have to put the ObjectId and inside the ID itself of the document.
I found on the loopback 4 documentation that if I'm saving or relating data which is store with objectId, I have to set the property dataType:ObjectId, but still dosnt work.
My Block model:
import {Entity, model, property, hasMany} from '#loopback/repository';
import {Site} from './site.model';
#model({
settings: {
strict: false,
mongodb: {
collection: 'bloques',
},
},
})
export class Block extends Entity {
#property({
type: 'string',
id: true,
defaultFn: 'uuidv4',
mongodb: {dataType: 'ObjectId'},
})
id: string;
#property({
type: 'date',
required: true,
})
dateBlock: string;
#property({
type: 'object',
required: true,
})
statuses: object;
#hasMany(() => Site)
minedIds: Site[];
/* #hasMany(() => Site)
minedIds: Site[];*/
[prop: string]: any;
constructor(data?: Partial<Block>) {
super(data);
}
}
export interface BlockRelations {
// describe navigational properties here
}
export type BlockWithRelations = Block & BlockRelations;
My Site Model:
import {Entity, hasMany, model, property} from '#loopback/repository';
import {CachimbaModel} from './cachimba-model.model';
#model({
settings: {
// model definition goes in here
mongodb: {collection: 'minadas'},
},
})
export class Site extends Entity {
#property({
type: 'string',
id: true,
generated: true,
mongodb: {dataType: 'ObjectId'},
})
id?: string;
#property({
type: 'date',
required: true,
})
lastUpdate: string;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'string',
required: true,
})
logo: string;
#hasMany(() => CachimbaModel)
data: CachimbaModel[];
#property({
type: 'string',
dataType: 'ObjectId',
})
blockId?: string;
constructor(data?: Partial<Site>) {
super(data);
}
}
export interface SiteRelations {
// describe navigational properties here
}
export type SiteWithRelations = Site & SiteRelations;
And their respective repos are as the following:
block-repo
import {Getter, inject} from '#loopback/core';
import {DefaultCrudRepository, repository, HasManyRepositoryFactory} from '#loopback/repository';
import {DbDataSource} from '../datasources';
import {Block, BlockRelations, Site} from '../models';
import {SiteRepository} from './site.repository';
export class BlockRepository extends DefaultCrudRepository<
Block,
typeof Block.prototype.id,
BlockRelations
> {
public readonly minedIds: HasManyRepositoryFactory<Site, typeof Block.prototype.id>;
constructor(
#inject('datasources.DbDataSource') dataSource: DbDataSource,
#repository.getter('SiteRepository')
protected siteRepositoryGetter: Getter<SiteRepository>,
) {
super(Block, dataSource);
this.minedIds = this.createHasManyRepositoryFactoryFor('minedIds', siteRepositoryGetter,);
this.registerInclusionResolver('minedIds', this.minedIds.inclusionResolver);
}
}
site-repo
import {Getter, inject} from '#loopback/core';
import {
DefaultCrudRepository,
HasManyRepositoryFactory,
repository,
} from '#loopback/repository';
import {DbDataSource} from '../datasources';
import {CachimbaModel, Site, SiteRelations} from '../models';
import {CachimbaModelRepository} from './cachimba-model.repository';
export class SiteRepository extends DefaultCrudRepository<
Site,
typeof Site.prototype.id,
SiteRelations
> {
public readonly data: HasManyRepositoryFactory<
CachimbaModel,
typeof Site.prototype.id
>;
constructor(
#inject('datasources.DbDataSource') dataSource: DbDataSource,
#repository.getter('CachimbaModelRepository')
protected cachimbaModelRepositoryGetter: Getter<CachimbaModelRepository>,
) {
super(Site, dataSource);
this.data = this.createHasManyRepositoryFactoryFor(
'data',
cachimbaModelRepositoryGetter,
);
this.registerInclusionResolver('data', this.data.inclusionResolver);
}
}
I try to query and obtain the nested data from the block controller using an include as the following:
And my database has the following values:
I will apreaciate your help for real! Thanks in advance!
My bad, I didnt have to set the ids on minedIds property, I had to set it on the destinatary model property opposite to the hasMany
Related
I have a typeorm entity that uses single table inheritance:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
}
and some child entities:
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
#ChildEntity()
#ObjectType()
class SoftwareOrganisation {
}
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
I'm wondering how I can get the type and the child properties from the parent organisation so that I can do something like:
const organisation = await OrganisationEntity.findOne()
if(organisation.type === "medicalOrganisation"){
...
}
But it seems I'm not allowed to access the type property through the parent. Does anyone know how this can be done?
I'd prefer not to use instanceof because it requires the child entities and is causing circular dependencies.
You've got two options. Either use .createQueryBuilder and get the item using getRawOne and the returned object would contain a field named OrganisationEntity_type which can be used to do the checks. It's value would either be 'MedicalOrganisation', or 'SoftwareOrganisation' and so on.
const orgRepository: Repository<OrganisationEntity> = getRepository(OrganisationEntity);
let organisation = await orgRepository.createQueryBuilder().getRawOne();
// organisation = {
// OrganisationEntity_name: 'name',
// OrganisationEntity_users: [
// {},
// {}
// ],
// OrganisationEntity_type: 'MedicalOrganisation'
// }
Or, you could add the field type in the OrganisationEntity itself like this:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
#Column
type: string;
}
and do a straightforward:
let organisation = await OrganisationEntity.findOne();
// organisation =
// MedicalOrganisation {
// name: 'name',
// users: [{}, {}],
// type: 'MedicalOrganisation'
// }
and you get the type field in the object itself.
I am trying to create a time series schema using mongoose in Nestjs. Here's my schema -
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type NewUserModel = NewUser & Document;
#Schema({
timeseries: {
timeField: 'timeStamp',
metaField: 'Name',
granularity: 'seconds',
},
})
export class NewUser {
#Prop({ required: true })
readonly Name: string;
#Prop({ required: true })
readonly Skills: string;
#Prop({ type: Date })
readonly timeStamp: Date;
}
export const NewUserSchema = SchemaFactory.createForClass(NewUser);
And here's the json request that I am sending through postman -
{
"Name": "Nikolia Mangan",
"Skills": "Management",
"timeStamp": "2022-06-22T06:22:57.606+00:00"
}
This creates a collection but not a time series collection. I can confirm that by running this command in the shell -
db.runCommand( { listCollections: 1.0 } )
Which in turn returns the output -
"type" : "collection",
I have the following Schema:
#Schema()
export class Category extends Document {
#Prop({ required: 'Category name is required' })
name: string;
#Prop({ default: true })
active: boolean;
}
export const CategorySchema = SchemaFactory.createForClass(Category);
And another one that imports this one:
#Schema()
export class Skill extends Document {
#Prop({ required: 'Skill name is required' })
name: string;
#Prop({ default: true })
active: boolean;
#Prop()
category: typeof CategorySchema;
}
export const SkillSchema = SchemaFactory.createForClass(Skill);
And the following query to find the data:
let objects = await this.model
.find(options)
.collation({ locale: "pt" })
.exec();
Everything seems to work, but when I call passing the options to search for the nested object id it doesn't find anything:
const options = {
'category._id': id,
active: true,
};
When I query other property than the nested id, it seems to work fine:
const options = {
'category.name': 'test',
active: true,
};
So, maybe my schema is wrong?
Any clues here?
As Documented on TypeOrm FrameWork Repository.save should save/insert new values and ignore/update the existing once,
But now I'm facing a problem that it's thrown a duplication error on existing value and stoping the whole inserting! ( I have a unique column called key )
My entity:
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { Source } from '../sources/source.entity';
#Entity({ name: 'articles' })
export class Article {
#PrimaryGeneratedColumn()
id: number;
#Column()
title: string;
#Column({
nullable: true
})
image: string | null;
#Column({
type: "text",
})
url: string;
#Column({
type: "varchar",
unique: true
})
key: string;
#Column({
type: 'datetime',
nullable: true
})
date: Date | null;
#ManyToOne(type => Source, source => source.articles, {eager: true})
#JoinColumn({name: 'source'})
source: Source;
#Column({
type: `text`,
nullable: true
})
description: string | null
}
My Service:
constructor(
#InjectRepository(Article) private readonly articleRepository: Repository<Article>,
private readonly articlesScraper: BlogScraperService
) {
}
async clonningFromScraper() {
let articles = await this.articlesScraper.articles('1');
articles = articles.map(article => ({ ...article, key: decodeURIComponent(article.url).substring(0, 255) }));
return this.articleRepository
.save(articles);
}
I have ended up solving this by RAW SQL query using the following
return this.articleRepository.query(
"INSERT IGNORE INTO articles ( title, date, url, image, source, description, _key ) VALUES ?", [querableArticles]);
https://loopback.io/doc/en/lb4/HasMany-relation.html
I followed this steps and then tried to get data with include but I get 500.
500 Error: Invalid "filter.include" entries: {"relation":"ranks"}
What I want is to get games object with its related ranks.
Rank Model
import { Entity, model, property, belongsTo } from '#loopback/repository';
import { Game, GameWithRelations } from './game.model';
#model({ settings: { strict: 'filter' } })
export class Rank extends Entity {
#property({
type: 'string',
id: true,
})
id?: string;
#property({
type: 'string',
})
name?: string;
#property({
type: 'string',
})
shortName?: string;
#property({
type: 'string',
})
avatar?: string;
#belongsTo(() => Game)
gameId: string;
constructor(data?: Partial<Rank>) {
super(data);
}
}
export interface RankRelations {
game?: GameWithRelations;
}
export type RankWithRelations = Rank & RankRelations;
Game Model
import { Entity, model, property, embedsMany, hasMany } from '#loopback/repository';
import { Rank, RankWithRelations } from './rank.model';
import { HasMany } from 'loopback-datasource-juggler';
#model({ settings: { strict: 'filter' } })
export class Game extends Entity {
#property({
type: 'string',
id: true,
})
id?: string;
#property({
type: 'string',
required: true,
})
name?: string;
#property({
type: 'string',
})
shortName?: string;
#property({
type: 'string',
})
avatar?: string;
#hasMany<Rank>(() => Rank, { keyTo: 'gameId' })
ranks?: Rank[];
constructor(data?: Partial<Game>) {
super(data);
}
}
export interface GameRelations {
}
export type GameWithRelations = Game & GameRelations;
Game Controller
// in this method
// 500 Error: Invalid "filter.include" entries: {"relation":"ranks"}
#get('/games/{id}')
async findById(#param.path.string('id') id: string): Promise<Game> {
return await this.gameRepository.findById(id, { include: [{ relation: 'ranks' }] });
}
Please run your application with DEBUG=loopback:repository:relation-helpers, that way you will get a debug message explaining why filter.include entry was rejected.
You can find the code building the error message here:
https://github.com/strongloop/loopback-next/blob/97ba7893e253bfc2967ac08e408b211c9b9b7f40/packages/repository/src/relations/relation.helpers.ts#L96-L100
The most likely cause: your GameRepository does not have any InclusionResolver registered for ranks relation.
Please refer to our todo-list example to see how to register inclusion resolver. Cross-posting from https://github.com/strongloop/loopback-next/blob/97ba7893e253bfc2967ac08e408b211c9b9b7f40/examples/todo-list/src/repositories/todo-list.repository.ts#L41-L46:
this.todos = this.createHasManyRepositoryFactoryFor(
'todos',
todoRepositoryGetter,
);
this.registerInclusionResolver('todos', this.todos.inclusionResolver);