metmirr пре 4 година
родитељ
комит
c1d09f2175

+ 19 - 12
query-node/mappings/content-directory/decode.ts

@@ -6,11 +6,15 @@ import {
   IBatchOperation,
   ICreateEntityOperation,
   IEntity,
+  IReference,
 } from '../types'
+import Debug from 'debug'
 
 import { ParametrizedClassPropertyValue, UpdatePropertyValuesOperation } from '@joystream/types/content-directory'
 import { createType } from '@joystream/types'
 
+const debug = Debug('mappings:cd:decode')
+
 function stringIfyEntityId(event: SubstrateEvent): string {
   const { 1: entityId } = event.params
   return entityId.value as string
@@ -20,16 +24,18 @@ function setProperties<T>({ extrinsic, blockNumber }: SubstrateEvent, propNamesW
   if (extrinsic === undefined) throw Error('Undefined extrinsic')
 
   const { 3: newPropertyValues } = extrinsic!.args
-  const properties: { [key: string]: any } = {}
+  const properties: { [key: string]: any; reference?: IReference } = {}
 
   for (const [k, v] of Object.entries(newPropertyValues.value)) {
     const propertyName = propNamesWithId[k]
-    const propertyValue = createType('InputPropertyValue', v as any)
-      .asType('Single')
-      .value.toJSON()
-    properties[propertyName] = propertyValue
+    const singlePropVal = createType('InputPropertyValue', v as any).asType('Single')
+    properties[propertyName] = singlePropVal.isOfType('Reference')
+      ? { entityId: singlePropVal.asType('Reference').toJSON(), existing: true }
+      : singlePropVal.value.toJSON()
   }
   properties.version = blockNumber
+
+  debug(`Entity properties: ${JSON.stringify(properties)}`)
   return properties as T
 }
 
@@ -49,15 +55,15 @@ function getClassEntity(event: SubstrateEvent): IClassEntity {
  * @param propertyNamesWithId
  */
 function setEntityPropertyValues<T>(properties: IProperty[], propertyNamesWithId: IPropertyIdWithName): T {
-  const entityProperties: { [key: string]: any } = {}
+  const entityProperties: { [key: string]: any; reference?: IReference } = {}
 
   for (const [propId, propName] of Object.entries(propertyNamesWithId)) {
     // get the property value by id
-    const p = properties.find((p) => p.propertyId === propId)
-    const propertyValue = p ? p.value : undefined
-    entityProperties[propName] = propertyValue
+    const p = properties.find((p) => p.id === propId)
+    if (!p) continue
+    entityProperties[propName] = p.reference ? p.reference : p.value
   }
-  // console.log(entityProperties);
+  // debug(`Entity properties ${JSON.stringify(entityProperties)}`)
   return entityProperties as T
 }
 
@@ -70,6 +76,7 @@ function getEntityProperties(propertyValues: ParametrizedClassPropertyValue[]):
     const v = createType('ParametrizedPropertyValue', pv.value)
     const propertyId = pv.in_class_index.toJSON()
 
+    let reference
     let value
     if (v.isOfType('InputPropertyValue')) {
       const inputPropVal = v.asType('InputPropertyValue')
@@ -77,13 +84,13 @@ function getEntityProperties(propertyValues: ParametrizedClassPropertyValue[]):
         ? inputPropVal.asType('Single').value.toJSON()
         : inputPropVal.asType('Vector').value.toJSON()
     } else if (v.isOfType('InternalEntityJustAdded')) {
-      // const inputPropVal = v.asType('InternalEntityJustAdded');
       value = v.asType('InternalEntityJustAdded').toJSON()
+      reference = { entityId: value as number, existing: false }
     } else {
       // TODO: Add support for v.asType('InternalEntityVec')
       throw Error('InternalEntityVec property type is not supported yet!')
     }
-    properties.push({ propertyId: `${propertyId}`, value })
+    properties.push({ id: `${propertyId}`, value, reference })
   })
   return properties
 }

+ 125 - 32
query-node/mappings/content-directory/entity-helper.ts

@@ -65,8 +65,18 @@ async function createChannel(
   channel.isPublic = p.isPublic
   channel.coverPhotoUrl = p.coverPhotoURL
   channel.avatarPhotoUrl = p.avatarPhotoURL
-  if (p.language !== undefined)
-    channel.language = await getOrCreate.language({ db, block, id }, classEntityMap, p.language)
+
+  const { language } = p
+  if (language !== undefined) {
+    if (language.existing) {
+      const r = await db.get(Language, { where: { id: language.entityId.toString() } })
+      if (!r) throw Error(`Language entity not found`)
+      channel.language = r
+    } else {
+      // Language entity id is based on the index so we increase the entityid to make it correct
+      channel.language = await getOrCreate.language({ db, block, id }, classEntityMap, language.entityId)
+    }
+  }
   channel.happenedIn = await createBlockOrGetFromDatabase(db, block)
   await db.save(channel)
   return channel
@@ -169,11 +179,25 @@ async function createVideoMedia(
   videoMedia.size = p.size
   videoMedia.version = block
   const { encoding, location } = p
-  if (encoding !== undefined)
-    videoMedia.encoding = await getOrCreate.videoMediaEncoding({ db, block, id }, classEntityMap, encoding)
+  if (encoding !== undefined) {
+    if (encoding.existing) {
+      const e = await db.get(VideoMediaEncoding, { where: { id: encoding.entityId.toString() } })
+      if (!e) throw Error(`VideoMediaEncoding not found: ${encoding.entityId}`)
+      videoMedia.encoding = e
+    } else {
+      videoMedia.encoding = await getOrCreate.videoMediaEncoding({ db, block, id }, classEntityMap, encoding.entityId)
+    }
+  }
   if (location !== undefined) {
-    videoMedia.location = await getOrCreate.mediaLocation({ db, block, id }, classEntityMap, location)
+    if (location.existing) {
+      const l = await db.get(MediaLocation, { where: { id: location.entityId.toString() } })
+      if (!l) throw Error(`MediaLocation not found: ${location.entityId}`)
+      videoMedia.location = l
+    } else {
+      videoMedia.location = await getOrCreate.mediaLocation({ db, block, id }, classEntityMap, location.entityId)
+    }
   }
+
   videoMedia.happenedIn = await createBlockOrGetFromDatabase(db, block)
   await db.save(videoMedia)
   return videoMedia
@@ -200,11 +224,51 @@ async function createVideo({ db, block, id }: IDBBlockId, classEntityMap: ClassE
   video.version = block
 
   const { language, license, category, channel, media } = p
-  if (language !== undefined) video.language = await getOrCreate.language({ db, block, id }, classEntityMap, language)
-  if (license !== undefined) video.license = await getOrCreate.license({ db, block, id }, classEntityMap, license)
-  if (category !== undefined) video.category = await getOrCreate.category({ db, block, id }, classEntityMap, category)
-  if (channel !== undefined) video.channel = await getOrCreate.channel({ db, block, id }, classEntityMap, channel)
-  if (media !== undefined) video.media = await getOrCreate.videoMedia({ db, block, id }, classEntityMap, media)
+  if (language !== undefined) {
+    if (language.existing) {
+      const l = await db.get(Language, { where: { id: language.entityId.toString() } })
+      if (!l) throw Error(`Language entity not found`)
+      video.language = l
+    } else {
+      video.language = await getOrCreate.language({ db, block, id }, classEntityMap, language.entityId)
+    }
+  }
+  if (license !== undefined) {
+    if (license.existing) {
+      const lic = await db.get(License, { where: { id: license.entityId.toString() } })
+      if (!lic) throw Error(`License entity not found`)
+      video.license = lic
+    } else {
+      video.license = await getOrCreate.license({ db, block, id }, classEntityMap, license.entityId)
+    }
+  }
+  if (category !== undefined) {
+    if (category.existing) {
+      const ca = await db.get(Category, { where: { id: category.entityId.toString() } })
+      if (!ca) throw Error(`Category not found`)
+      video.category = ca
+    } else {
+      video.category = await getOrCreate.category({ db, block, id }, classEntityMap, category.entityId)
+    }
+  }
+  if (channel !== undefined) {
+    if (channel.existing) {
+      const c = await db.get(Channel, { where: { id: channel.entityId.toString() } })
+      if (!c) throw Error(`Channel not found: ${channel.entityId}`)
+      video.channel = c
+    } else {
+      video.channel = await getOrCreate.channel({ db, block, id }, classEntityMap, channel.entityId)
+    }
+  }
+  if (media !== undefined) {
+    if (media.existing) {
+      const m = await db.get(VideoMedia, { where: { id: media.entityId.toString() } })
+      if (!m) throw Error(`VideoMedia not found: ${media.entityId}`)
+      video.media = m
+    } else {
+      video.media = await getOrCreate.videoMedia({ db, block, id }, classEntityMap, media.entityId)
+    }
+  }
 
   video.happenedIn = await createBlockOrGetFromDatabase(db, block)
   await db.save<Video>(video)
@@ -254,14 +318,28 @@ async function createLicense(
 
   const license = new License()
   license.id = id
-  if (knownLicense !== undefined)
-    license.knownLicense = await getOrCreate.knownLicense({ db, block, id }, classEntityMap, knownLicense)
-  if (userDefinedLicense !== undefined)
-    license.userdefinedLicense = await getOrCreate.userDefinedLicense(
-      { db, block, id },
-      classEntityMap,
-      userDefinedLicense
-    )
+  if (knownLicense !== undefined) {
+    if (knownLicense.existing) {
+      const kl = await db.get(KnownLicense, { where: { id: knownLicense.entityId.toString() } })
+      if (!kl) throw Error(`KnownLicense not found: ${knownLicense.entityId}`)
+      license.knownLicense = kl
+    } else {
+      license.knownLicense = await getOrCreate.knownLicense({ db, block, id }, classEntityMap, knownLicense.entityId)
+    }
+  }
+  if (userDefinedLicense !== undefined) {
+    if (userDefinedLicense.existing) {
+      const udl = await db.get(UserDefinedLicense, { where: { id: userDefinedLicense.entityId.toString() } })
+      if (!udl) throw Error(`UserDefinedLicense not found: ${userDefinedLicense.entityId}`)
+      license.userdefinedLicense = udl
+    } else {
+      license.userdefinedLicense = await getOrCreate.userDefinedLicense(
+        { db, block, id },
+        classEntityMap,
+        userDefinedLicense.entityId
+      )
+    }
+  }
   license.happenedIn = await createBlockOrGetFromDatabase(db, block)
   await db.save<License>(license)
   return license
@@ -276,32 +354,47 @@ async function createMediaLocation(
 
   const location = new MediaLocation()
   location.id = id
-  if (httpMediaLocation !== undefined)
-    location.httpMediaLocation = await getOrCreate.httpMediaLocation(
-      { db, block, id },
-      classEntityMap,
-      httpMediaLocation
-    )
-  if (joystreamMediaLocation !== undefined)
-    location.joystreamMediaLocation = await getOrCreate.joystreamMediaLocation(
-      { db, block, id },
-      classEntityMap,
-      joystreamMediaLocation
-    )
+  console.log(p)
+  if (httpMediaLocation !== undefined) {
+    const { existing, entityId } = httpMediaLocation
+    if (existing) {
+      const hml = await db.get(HttpMediaLocation, { where: { id: entityId.toString() } })
+      if (!hml) throw Error(`HttpMediaLocation not found: ${entityId}`)
+    } else {
+      location.httpMediaLocation = await getOrCreate.httpMediaLocation({ db, block, id }, classEntityMap, entityId)
+    }
+  }
+  if (joystreamMediaLocation !== undefined) {
+    const { entityId, existing } = joystreamMediaLocation
+    if (existing) {
+      const jml = await db.get(JoystreamMediaLocation, { where: { id: entityId.toString() } })
+      if (!jml) throw Error(`JoystreamMediaLocation not found: ${entityId}`)
+      location.joystreamMediaLocation = jml
+    } else {
+      location.joystreamMediaLocation = await getOrCreate.joystreamMediaLocation(
+        { db, block, id },
+        classEntityMap,
+        entityId
+      )
+    }
+  }
   location.happenedIn = await createBlockOrGetFromDatabase(db, block)
   await db.save<License>(location)
   return location
 }
 
 async function batchCreateClassEntities(db: DB, block: number, operations: ICreateEntityOperation[]): Promise<void> {
+  let entityId = 1 // Entity id starts at 1
   // Create entities before adding schema support
-  operations.map(async ({ classId }, index) => {
+  operations.map(async ({ classId }) => {
     const c = new ClassEntity()
-    c.id = index.toString() // entity id
+    c.id = entityId.toString()
     c.classId = classId
     c.version = block
     c.happenedIn = await createBlockOrGetFromDatabase(db, block)
     await db.save<ClassEntity>(c)
+
+    entityId++
   })
 }
 

+ 71 - 190
query-node/mappings/content-directory/get-or-create.ts

@@ -55,12 +55,15 @@ import {
   createVideoMediaEncoding,
 } from './entity-helper'
 
-function findEntity(entityId: number, newlyCreatedEntities: IEntity[]): IEntity | undefined {
+function generateEntityIdFromIndex(index: number): string {
+  return `${index + 1}`
+}
+
+function findEntity(entityId: number, className: string, classEntityMap: ClassEntityMap): IEntity {
+  const newlyCreatedEntities = classEntityMap.get(className)
+  if (newlyCreatedEntities === undefined) throw Error(`Couldn't find '${className}' entities in the classEntityMap`)
   const entity = newlyCreatedEntities.find((e) => e.indexOf === entityId)
-  if (!entity) {
-    console.log(`Unknown entity id: ${entityId}`)
-    return
-  }
+  if (!entity) throw Error(`Unknown ${className} entity id: ${entityId}`)
   return entity
 }
 
@@ -69,20 +72,11 @@ async function language(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<Language> {
-  let record = await db.get(Language, { where: { id: entityId.toString() } })
-  if (record) return record
-  const newlyCreatedEntities = classEntityMap.get('Language')
-
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown Language entity id`)
-
-    record = await createLanguage(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<ILanguage>(entity.properties, languagePropertyNamesWIthId)
-    )
-  }
-  if (!record) throw Error(`Language entity not found on the database`)
+  const entity = findEntity(entityId, 'Language', classEntityMap)
+  const record = await createLanguage(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<ILanguage>(entity.properties, languagePropertyNamesWIthId)
+  )
   removeInsertedEntity('Language', entityId, classEntityMap)
   return record
 }
@@ -92,20 +86,11 @@ async function videoMediaEncoding(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<VideoMediaEncoding> {
-  let record = await db.get(VideoMediaEncoding, { where: { id: entityId.toString() } })
-  if (record) return record
-  const newlyCreatedEntities = classEntityMap.get('VideoMediaEncoding')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown VideoMediaEncoding entity id`)
-
-    record = await createVideoMediaEncoding(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<IVideoMediaEncoding>(entity.properties, videoMediaEncodingPropertyNamesWithId)
-    )
-  }
-
-  if (!record) throw Error(`VideoMediaEncoding entity not found on the database`)
+  const entity = findEntity(entityId, 'VideoMediaEncoding', classEntityMap)
+  const record = await createVideoMediaEncoding(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<IVideoMediaEncoding>(entity.properties, videoMediaEncodingPropertyNamesWithId)
+  )
   removeInsertedEntity('VideoMediaEncoding', entityId, classEntityMap)
   return record
 }
@@ -115,22 +100,12 @@ async function videoMedia(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<VideoMedia> {
-  let record = await db.get(VideoMedia, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('VideoMedia')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown VideoMedia entity id`)
-
-    record = await createVideoMedia(
-      { db, block, id: entityId.toString() },
-      classEntityMap,
-      decode.setEntityPropertyValues<IVideoMedia>(entity.properties, videoPropertyNamesWithId)
-    )
-  }
-
-  if (!record) throw Error(`Video entity not found on the database`)
+  const entity = findEntity(entityId, 'VideoMedia', classEntityMap)
+  const record = await createVideoMedia(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    classEntityMap,
+    decode.setEntityPropertyValues<IVideoMedia>(entity.properties, videoPropertyNamesWithId)
+  )
   removeInsertedEntity('VideoMedia', entityId, classEntityMap)
   return record
 }
@@ -139,25 +114,12 @@ async function knownLicense(
   { db, block }: IDBBlockId,
   classEntityMap: ClassEntityMap,
   entityId: number
-): Promise<KnownLicense | undefined> {
-  let record = await db.get(KnownLicense, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('KnownLicense')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) {
-      console.log(`Unknown KnownLicense entity id`)
-      return
-    }
-
-    record = await createKnownLicense(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<IKnownLicense>(entity.properties, knownLicensePropertyNamesWIthId)
-    )
-  }
-
-  if (!record) throw Error(`KnownLicense entity not found on the database`)
+): Promise<KnownLicense> {
+  const entity = findEntity(entityId, 'KnownLicense', classEntityMap)
+  const record = await createKnownLicense(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<IKnownLicense>(entity.properties, knownLicensePropertyNamesWIthId)
+  )
   removeInsertedEntity('KnownLicense', entityId, classEntityMap)
   return record
 }
@@ -165,46 +127,22 @@ async function userDefinedLicense(
   { db, block }: IDBBlockId,
   classEntityMap: ClassEntityMap,
   entityId: number
-): Promise<UserDefinedLicense | undefined> {
-  let record = await db.get(UserDefinedLicense, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('UserDefinedLicense')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) {
-      console.log(`Unknown UserDefinedLicense entity id`)
-      return
-    }
-    record = await createUserDefinedLicense(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<IUserDefinedLicense>(entity.properties, userDefinedLicensePropertyNamesWithId)
-    )
-  }
-  if (!record) {
-    console.log(`UserDefinedLicense entity not found on the database`)
-    return
-  }
+): Promise<UserDefinedLicense> {
+  const entity = findEntity(entityId, 'UserDefinedLicense', classEntityMap)
+  const record = await createUserDefinedLicense(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<IUserDefinedLicense>(entity.properties, userDefinedLicensePropertyNamesWithId)
+  )
   removeInsertedEntity('UserDefinedLicense', entityId, classEntityMap)
   return record
 }
 async function channel({ db, block }: IDBBlockId, classEntityMap: ClassEntityMap, entityId: number): Promise<Channel> {
-  let record = await db.get(Channel, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('Channel')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown Channel entity id`)
-
-    record = await createChannel(
-      { db, block, id: entityId.toString() },
-      classEntityMap,
-      decode.setEntityPropertyValues<IChannel>(entity.properties, channelPropertyNamesWithId)
-    )
-  }
-
-  if (!record) throw Error(`Channel entity not found on the database`)
+  const entity = findEntity(entityId, 'Channel', classEntityMap)
+  const record = await createChannel(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    classEntityMap,
+    decode.setEntityPropertyValues<IChannel>(entity.properties, channelPropertyNamesWithId)
+  )
   removeInsertedEntity('Channel', entityId, classEntityMap)
   return record
 }
@@ -213,21 +151,11 @@ async function category(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<Category> {
-  let record = await db.get(Category, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('Category')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown Category entity id`)
-
-    record = await createCategory(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<ICategory>(entity.properties, CategoryPropertyNamesWithId)
-    )
-  }
-
-  if (!record) throw Error(`Category entity not found on the database`)
+  const entity = findEntity(entityId, 'Category', classEntityMap)
+  const record = await createCategory(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<ICategory>(entity.properties, CategoryPropertyNamesWithId)
+  )
   removeInsertedEntity('Category', entityId, classEntityMap)
   return record
 }
@@ -237,25 +165,11 @@ async function httpMediaLocation(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<HttpMediaLocation | undefined> {
-  let record = await db.get(HttpMediaLocation, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('HttpMediaLocation')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) {
-      console.log(`Unknown HttpMediaLocation entity id`)
-      return
-    }
-    record = await createHttpMediaLocation(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<IHttpMediaLocation>(entity.properties, httpMediaLocationPropertyNamesWithId)
-    )
-  }
-  if (!record) {
-    console.log(`HttpMediaLocation entity not found on the database`)
-    return
-  }
+  const entity = findEntity(entityId, 'HttpMediaLocation', classEntityMap)
+  const record = await createHttpMediaLocation(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<IHttpMediaLocation>(entity.properties, httpMediaLocationPropertyNamesWithId)
+  )
   removeInsertedEntity('HttpMediaLocation', entityId, classEntityMap)
   return record
 }
@@ -265,47 +179,25 @@ async function joystreamMediaLocation(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<JoystreamMediaLocation | undefined> {
-  let record = await db.get(JoystreamMediaLocation, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('JoystreamMediaLocation')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown JoystreamMediaLocation entity id`)
-
-    record = await createJoystreamMediaLocation(
-      { db, block, id: entityId.toString() },
-      decode.setEntityPropertyValues<IJoystreamMediaLocation>(
-        entity.properties,
-        joystreamMediaLocationPropertyNamesWithId
-      )
+  const entity = findEntity(entityId, 'JoystreamMediaLocation', classEntityMap)
+  const record = await createJoystreamMediaLocation(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    decode.setEntityPropertyValues<IJoystreamMediaLocation>(
+      entity.properties,
+      joystreamMediaLocationPropertyNamesWithId
     )
-  }
-
-  if (!record) {
-    console.log(`JoystreamMediaLocation entity not found on the database`)
-    return
-  }
+  )
   removeInsertedEntity('JoystreamMediaLocation', entityId, classEntityMap)
   return record
 }
 
 async function license({ db, block }: IDBBlockId, classEntityMap: ClassEntityMap, entityId: number): Promise<License> {
-  let record = await db.get(License, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('License')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown License entity id`)
-
-    record = await createLicense(
-      { db, block, id: entityId.toString() },
-      classEntityMap,
-      decode.setEntityPropertyValues<ILicense>(entity.properties, licensePropertyNamesWithId)
-    )
-  }
-  if (!record) throw Error(`License entity not found on the database`)
+  const entity = findEntity(entityId, 'License', classEntityMap)
+  const record = await createLicense(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    classEntityMap,
+    decode.setEntityPropertyValues<ILicense>(entity.properties, licensePropertyNamesWithId)
+  )
   removeInsertedEntity('License', entityId, classEntityMap)
   return record
 }
@@ -315,23 +207,12 @@ async function mediaLocation(
   classEntityMap: ClassEntityMap,
   entityId: number
 ): Promise<MediaLocation> {
-  let record = await db.get(MediaLocation, { where: { id: entityId.toString() } })
-  if (record) return record
-
-  const newlyCreatedEntities = classEntityMap.get('MediaLocation')
-  if (newlyCreatedEntities) {
-    const entity = findEntity(entityId, newlyCreatedEntities)
-    if (!entity) throw Error(`Unknown MediaLocation entity id`)
-
-    record = await createMediaLocation(
-      { db, block, id: entityId.toString() },
-      classEntityMap,
-      decode.setEntityPropertyValues<IMediaLocation>(entity.properties, mediaLocationPropertyNamesWithId)
-    )
-  }
-  if (!record) {
-    throw Error(`MediaLocation entity not found on the database`)
-  }
+  const entity = findEntity(entityId, 'MediaLocation', classEntityMap)
+  const record = await createMediaLocation(
+    { db, block, id: generateEntityIdFromIndex(entityId) },
+    classEntityMap,
+    decode.setEntityPropertyValues<IMediaLocation>(entity.properties, mediaLocationPropertyNamesWithId)
+  )
   removeInsertedEntity('MediaLocation', entityId, classEntityMap)
   return record
 }

+ 4 - 3
query-node/mappings/content-directory/transaction.ts

@@ -65,7 +65,7 @@ import {
   updateMediaLocationEntityPropertyValues,
 } from './entity-helper'
 
-const debug = Debug('mappings:content-directory')
+const debug = Debug('mappings:cd:transaction')
 
 // eslint-disable-next-line @typescript-eslint/naming-convention
 export async function contentDirectory_TransactionCompleted(db: DB, event: SubstrateEvent): Promise<void> {
@@ -128,8 +128,9 @@ async function batchAddSchemaSupportToEntity(
     for (const entity of entities) {
       const { entityId, indexOf, properties } = entity
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-      const id = entityId ? entityId.toString() : indexOf!.toString()
-      const arg: IDBBlockId = { db, block, id }
+      const id = entityId !== undefined ? entityId : indexOf! + 1 // create entity id from index
+
+      const arg: IDBBlockId = { db, block, id: id.toString() }
 
       switch (className) {
         case ContentDirectoryKnownClasses.CATEGORY:

+ 26 - 15
query-node/mappings/types.ts

@@ -34,6 +34,11 @@ export interface MemberControllerAccount extends BaseJoystreamMember {
   controllerAccount: Buffer
 }
 
+export interface IReference {
+  entityId: number
+  existing: boolean
+}
+
 export interface IChannel {
   title: string
   description: string
@@ -41,7 +46,7 @@ export interface IChannel {
   avatarPhotoURL: string
   isPublic: boolean
   isCurated: boolean
-  language?: number
+  language?: IReference
 }
 
 export interface ICategory {
@@ -79,42 +84,42 @@ export interface IVideoMediaEncoding {
 }
 
 export interface IVideoMedia {
-  encoding?: number
+  encoding?: IReference
   pixelWidth: number
   pixelHeight: number
   size: number
-  location?: number
+  location?: IReference
 }
 
 export interface IVideo {
   // referenced entity's id
-  channel?: number
+  channel?: IReference
   // referenced entity's id
-  category?: number
+  category?: IReference
   title: string
   description: string
   duration: number
   skippableIntroDuration?: number
   thumbnailURL: string
-  language?: number
+  language?: IReference
   // referenced entity's id
-  media?: number
+  media?: IReference
   hasMarketing?: boolean
   publishedBeforeJoystream?: number
   isPublic: boolean
   isCurated: boolean
   isExplicit: boolean
-  license?: number
+  license?: IReference
 }
 
 export interface ILicense {
-  knownLicense?: number
-  userDefinedLicense?: number
+  knownLicense?: IReference
+  userDefinedLicense?: IReference
 }
 
 export interface IMediaLocation {
-  httpMediaLocation?: number
-  joystreamMediaLocation?: number
+  httpMediaLocation?: IReference
+  joystreamMediaLocation?: IReference
 }
 
 export enum OperationType {
@@ -145,9 +150,15 @@ export interface IBatchOperation {
 }
 
 export interface IProperty {
-  [propertyId: string]: any
-  // propertyId: string;
-  // value: any;
+  // PropertId: Value
+  // [propertyId: string]: any
+
+  id: string
+  value: any
+
+  // If reference.exising is false then reference.entityId is the index that entity is at
+  // in the transaction batch operation
+  reference?: IReference
 }
 
 export interface IEntity {