@ -17,58 +17,55 @@ const EventType = {
exports . EventType = EventType
exports . EventType = EventType
/ * *
/ * *
* Update link documents for a model - this is to be called by the model controller when a model is being changed .
* Creates the link view for the instance , this will overwrite the existing one , but this should only
* @ param { EventType } eventType states what type of model change is occurring , means this can be expanded upon in the
* be called if it is found that the view does not exist .
* future quite easily ( all updates go through one function ) .
* @ param { string } instanceId The instance to which the view should be added .
* @ param { string } instanceId The ID of the instance in which the model change is occurring .
* @ returns { Promise < void > } The view now exists , please note that the next view of this query will actually build it ,
* @ param { object } model The model which is changing , whether it is being deleted , created or updated .
* so it may be slow .
* @ returns { Promise < object > } When the update is complete this will respond successfully . Returns the model that was
* operated upon .
* /
* /
exports . updateLinksForModel = async ( { eventType , instanceId , model } ) => {
exports . createLinkView = async instanceId => {
// can't operate without these properties
const db = new CouchDB ( instanceId )
if ( instanceId == null || model == null ) {
const designDoc = await db . get ( "_design/database" )
return model
const view = {
}
map : function ( doc ) {
let linkController = new LinkController ( {
if ( doc . type === "link" ) {
instanceId ,
let doc1 = doc . doc1
modelId : model . _ id ,
let doc2 = doc . doc2
model ,
emit ( [ doc1 . modelId , 1 , doc1 . fieldName , doc1 . recordId ] , doc2 . recordId )
} )
emit ( [ doc2 . modelId , 1 , doc2 . fieldName , doc2 . recordId ] , doc1 . recordId )
if ( ! ( await linkController . doesModelHaveLinkedFields ( ) ) ) {
emit ( [ doc1 . modelId , 2 , doc1 . recordId ] , doc2 . recordId )
return model
emit ( [ doc2 . modelId , 2 , doc2 . recordId ] , doc1 . recordId )
}
} . toString ( ) ,
}
}
switch ( eventType ) {
designDoc . views = {
case EventType . MODEL_SAVE :
... designDoc . views ,
return await linkController . modelSaved ( )
by_link : view ,
case EventType . MODEL_DELETE :
return await linkController . modelDeleted ( )
default :
throw "Type of event is not known, linked record handler requires update."
}
}
await db . put ( designDoc )
}
}
/ * *
/ * *
* Update link documents for a record - this is to be called by the record controller when a record is being changed .
* Update link documents for a record or model - this is to be called by the API controller when a change is occurring .
* @ param { EventType } eventType states what type of record change is occurring , means this can be expanded upon in the
* @ param { string } eventType states what type of change which is occurring , means this can be expanded upon in the
* future quite easily ( all updates go through one function ) .
* future quite easily ( all updates go through one function ) .
* @ param { string } instanceId The ID of the instance in which the record updat e is occurring .
* @ param { string } instanceId The ID of the instance in which the chang e is occurring .
* @ param { object } record The record which is changing , e . g . created , updated or delet ed.
* @ param { string } modelId The ID of the of the model which is being chang ed.
* @ param { string } modelId The ID of the of the model which is being upda ted.
* * @ param { object | null } record The record which is changing , e . g . created , updated or dele ted.
* @ param { object | null } model If the model has already been retrieved this can be used to reduce database gets .
* @ param { object | null } model If the model has already been retrieved this can be used to reduce database gets .
* @ returns { Promise < object > } When the update is complete this will respond successfully . Returns the record that was
* @ returns { Promise < object > } When the update is complete this will respond successfully . Returns the record for
* operated upon , cleaned up and prepared for writing to DB .
* record operations and the model for model operations .
* /
* /
exports . updateLinksForRecord = async ( {
exports . updateLinks = async ( {
eventType ,
eventType ,
instanceId ,
instanceId ,
record ,
record ,
modelId ,
modelId ,
model ,
model ,
} ) => {
} ) => {
// can't operate without these properties
// make sure model ID is set
if ( instanceId == null || modelId == null || record = = null ) {
if ( model ! = null ) {
return recor d
modelId = model . _ i d
}
}
let linkController = new LinkController ( {
let linkController = new LinkController ( {
instanceId ,
instanceId ,
@ -85,6 +82,10 @@ exports.updateLinksForRecord = async ({
return await linkController . recordSaved ( )
return await linkController . recordSaved ( )
case EventType . RECORD_DELETE :
case EventType . RECORD_DELETE :
return await linkController . recordDeleted ( )
return await linkController . recordDeleted ( )
case EventType . MODEL_SAVE :
return await linkController . modelSaved ( )
case EventType . MODEL_DELETE :
return await linkController . modelDeleted ( )
default :
default :
throw "Type of event is not known, linked record handler requires update."
throw "Type of event is not known, linked record handler requires update."
}
}
@ -96,10 +97,10 @@ exports.updateLinksForRecord = async ({
* @ param { object [ ] } records A list records to be updated with link info .
* @ param { object [ ] } records A list records to be updated with link info .
* @ returns { Promise < object [ ] > } The updated records ( this may be the same if no links were found ) .
* @ returns { Promise < object [ ] > } The updated records ( this may be the same if no links were found ) .
* /
* /
exports . attachLinkInfoToRecords = async ( instanceId , records ) => {
exports . attachLinkInfo = async ( instanceId , records ) => {
let recordPromises = [ ]
let recordPromises = [ ]
for ( let record of records ) {
for ( let record of records ) {
recordPromises . push ( exports . attachLinkInfoToRecord ( instanceId , record ) )
recordPromises . push ( exports . attachLinkInfo ( instanceId , record ) )
}
}
return await Promise . all ( recordPromises )
return await Promise . all ( recordPromises )
}
}
@ -110,7 +111,20 @@ exports.attachLinkInfoToRecords = async (instanceId, records) => {
* @ param { object } record The record itself which is to be updated with info ( if applicable ) .
* @ param { object } record The record itself which is to be updated with info ( if applicable ) .
* @ returns { Promise < object > } The updated record ( this may be the same if no links were found ) .
* @ returns { Promise < object > } The updated record ( this may be the same if no links were found ) .
* /
* /
exports . attachLinkInfoToRecord = async ( instanceId , record ) => {
exports . attachLinkInfo = async ( instanceId , record ) => {
// first check if the record has any link fields
let hasLinkedRecords = false
for ( let fieldName of Object . keys ( record ) ) {
let field = record [ fieldName ]
if ( field != null && field . type === "link" ) {
hasLinkedRecords = true
break
}
}
// no linked records, can simply return
if ( ! hasLinkedRecords ) {
return record
}
const recordId = record . _ id
const recordId = record . _ id
const modelId = record . modelId
const modelId = record . modelId
// get all links for record, ignore fieldName for now
// get all links for record, ignore fieldName for now
@ -126,8 +140,8 @@ exports.attachLinkInfoToRecord = async (instanceId, record) => {
for ( let linkDoc of linkDocs ) {
for ( let linkDoc of linkDocs ) {
// work out which link pertains to this record
// work out which link pertains to this record
const doc = linkDoc . doc1 . recordId === recordId ? linkDoc . doc1 : linkDoc . doc2
const doc = linkDoc . doc1 . recordId === recordId ? linkDoc . doc1 : linkDoc . doc2
if ( record [ doc . fieldName ] == null || record [ doc . fieldName ] . count == null ) {
if ( record [ doc . fieldName ] . count == null ) {
record [ doc . fieldName ] = { type : "link" , count : 1 }
record [ doc . fieldName ] . count = 1
} else {
} else {
record [ doc . fieldName ] . count ++
record [ doc . fieldName ] . count ++
}
}
@ -135,28 +149,6 @@ exports.attachLinkInfoToRecord = async (instanceId, record) => {
return record
return record
}
}
exports . createLinkView = async instanceId => {
const db = new CouchDB ( instanceId )
const designDoc = await db . get ( "_design/database" )
const view = {
map : function ( doc ) {
if ( doc . type === "link" ) {
let doc1 = doc . doc1
let doc2 = doc . doc2
emit ( [ doc1 . modelId , 1 , doc1 . fieldName , doc1 . recordId ] , doc2 . recordId )
emit ( [ doc2 . modelId , 1 , doc2 . fieldName , doc2 . recordId ] , doc1 . recordId )
emit ( [ doc1 . modelId , 2 , doc1 . recordId ] , doc2 . recordId )
emit ( [ doc2 . modelId , 2 , doc2 . recordId ] , doc1 . recordId )
}
} . toString ( ) ,
}
designDoc . views = {
... designDoc . views ,
by_link : view ,
}
await db . put ( designDoc )
}
/ * *
/ * *
* Gets the linking documents , not the linked documents themselves .
* Gets the linking documents , not the linked documents themselves .
* @ param { string } instanceId The instance in which we are searching for linked records .
* @ param { string } instanceId The instance in which we are searching for linked records .