Moving views into a different location so they don't trigger tree creation and attempting to use in memory pouchDB to run views on the fly.
This commit is contained in:
parent
f5edc45570
commit
a38f83d0ec
|
@ -5,6 +5,7 @@ const {
|
||||||
generateRowID,
|
generateRowID,
|
||||||
DocumentTypes,
|
DocumentTypes,
|
||||||
InternalTables,
|
InternalTables,
|
||||||
|
generateMemoryViewID,
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const userController = require("../user")
|
const userController = require("../user")
|
||||||
const {
|
const {
|
||||||
|
@ -16,6 +17,8 @@ const { isEqual } = require("lodash")
|
||||||
const { validate, findRow } = require("./utils")
|
const { validate, findRow } = require("./utils")
|
||||||
const { fullSearch, paginatedSearch } = require("./internalSearch")
|
const { fullSearch, paginatedSearch } = require("./internalSearch")
|
||||||
const { getGlobalUsersFromMetadata } = require("../../../utilities/global")
|
const { getGlobalUsersFromMetadata } = require("../../../utilities/global")
|
||||||
|
const inMemoryViews = require("../../../db/inMemoryView")
|
||||||
|
const env = require("../../../environment")
|
||||||
|
|
||||||
const CALCULATION_TYPES = {
|
const CALCULATION_TYPES = {
|
||||||
SUM: "sum",
|
SUM: "sum",
|
||||||
|
@ -36,6 +39,40 @@ async function storeResponse(ctx, db, row, oldTable, table) {
|
||||||
return { row, table }
|
return { row, table }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doesn't do the outputProcessing
|
||||||
|
async function getRawTableData(ctx, db, tableId) {
|
||||||
|
let rows
|
||||||
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
|
await userController.fetchMetadata(ctx)
|
||||||
|
rows = ctx.body
|
||||||
|
} else {
|
||||||
|
const response = await db.allDocs(
|
||||||
|
getRowParams(tableId, null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
rows = response.rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getView(db, viewName) {
|
||||||
|
let viewInfo
|
||||||
|
if (env.SELF_HOSTED) {
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
viewInfo = designDoc.views[viewName]
|
||||||
|
} else {
|
||||||
|
viewInfo = await db.get(generateMemoryViewID(viewName))
|
||||||
|
if (viewInfo) {
|
||||||
|
viewInfo = viewInfo.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!viewInfo) {
|
||||||
|
throw "View does not exist."
|
||||||
|
}
|
||||||
|
return viewInfo
|
||||||
|
}
|
||||||
|
|
||||||
exports.patch = async ctx => {
|
exports.patch = async ctx => {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
@ -139,15 +176,28 @@ exports.fetchView = async ctx => {
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
const { calculation, group, field } = ctx.query
|
const { calculation, group, field } = ctx.query
|
||||||
const designDoc = await db.get("_design/database")
|
const viewInfo = await getView(db, viewName)
|
||||||
const viewInfo = designDoc.views[viewName]
|
|
||||||
if (!viewInfo) {
|
if (!viewInfo) {
|
||||||
throw "View does not exist."
|
throw "View does not exist."
|
||||||
}
|
}
|
||||||
const response = await db.query(`database/${viewName}`, {
|
let response
|
||||||
include_docs: !calculation,
|
// TODO: make sure not self hosted in Cloud
|
||||||
group: !!group,
|
if (!env.SELF_HOSTED) {
|
||||||
})
|
response = await db.query(`database/${viewName}`, {
|
||||||
|
include_docs: !calculation,
|
||||||
|
group: !!group,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const tableId = viewInfo.meta.tableId
|
||||||
|
const data = await getRawTableData(ctx, db, tableId)
|
||||||
|
response = await inMemoryViews.runView(
|
||||||
|
appId,
|
||||||
|
viewInfo,
|
||||||
|
calculation,
|
||||||
|
group,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let rows
|
let rows
|
||||||
if (!calculation) {
|
if (!calculation) {
|
||||||
|
@ -191,19 +241,9 @@ exports.fetch = async ctx => {
|
||||||
const appId = ctx.appId
|
const appId = ctx.appId
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
|
|
||||||
let rows,
|
const tableId = ctx.params.tableId
|
||||||
table = await db.get(ctx.params.tableId)
|
let table = await db.get(tableId)
|
||||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
let rows = await getRawTableData(ctx, db, tableId)
|
||||||
await userController.fetchMetadata(ctx)
|
|
||||||
rows = ctx.body
|
|
||||||
} else {
|
|
||||||
const response = await db.allDocs(
|
|
||||||
getRowParams(ctx.params.tableId, null, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
rows = response.rows.map(row => row.doc)
|
|
||||||
}
|
|
||||||
return outputProcessing(ctx, table, rows)
|
return outputProcessing(ctx, table, rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,27 @@ const viewTemplate = require("./viewBuilder")
|
||||||
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
||||||
const exporters = require("./exporters")
|
const exporters = require("./exporters")
|
||||||
const { fetchView } = require("../row")
|
const { fetchView } = require("../row")
|
||||||
const { ViewNames } = require("../../../db/utils")
|
const {
|
||||||
|
ViewNames,
|
||||||
|
generateMemoryViewID,
|
||||||
|
getMemoryViewParams,
|
||||||
|
} = require("../../../db/utils")
|
||||||
|
const env = require("../../../environment")
|
||||||
|
|
||||||
const controller = {
|
async function getView(db, viewName) {
|
||||||
fetch: async ctx => {
|
if (env.SELF_HOSTED) {
|
||||||
const db = new CouchDB(ctx.appId)
|
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const response = []
|
return designDoc.views[viewName]
|
||||||
|
} else {
|
||||||
|
const viewDoc = await db.get(generateMemoryViewID(viewName))
|
||||||
|
return viewDoc.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getViews(db) {
|
||||||
|
const response = []
|
||||||
|
if (env.SELF_HOSTED) {
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
for (let name of Object.keys(designDoc.views)) {
|
for (let name of Object.keys(designDoc.views)) {
|
||||||
// Only return custom views, not built ins
|
// Only return custom views, not built ins
|
||||||
if (Object.values(ViewNames).indexOf(name) !== -1) {
|
if (Object.values(ViewNames).indexOf(name) !== -1) {
|
||||||
|
@ -21,30 +34,91 @@ const controller = {
|
||||||
...designDoc.views[name],
|
...designDoc.views[name],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const views = (
|
||||||
|
await db.allDocs(
|
||||||
|
getMemoryViewParams({
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
for (let viewDoc of views) {
|
||||||
|
response.push({
|
||||||
|
name: viewDoc.name,
|
||||||
|
...viewDoc.view,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
ctx.body = response
|
async function saveView(db, originalName, viewToSave, viewTemplate) {
|
||||||
|
if (env.SELF_HOSTED) {
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
designDoc.views = {
|
||||||
|
...designDoc.views,
|
||||||
|
[viewToSave.name]: viewTemplate,
|
||||||
|
}
|
||||||
|
// view has been renamed
|
||||||
|
if (originalName) {
|
||||||
|
delete designDoc.views[originalName]
|
||||||
|
}
|
||||||
|
await db.put(designDoc)
|
||||||
|
} else {
|
||||||
|
const id = generateMemoryViewID(viewToSave.name)
|
||||||
|
const originalId = originalName ? generateMemoryViewID(originalName) : null
|
||||||
|
const viewDoc = {
|
||||||
|
_id: id,
|
||||||
|
view: viewTemplate,
|
||||||
|
name: viewToSave.name,
|
||||||
|
tableId: viewTemplate.meta.tableId,
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const old = await db.get(id)
|
||||||
|
if (originalId) {
|
||||||
|
const originalDoc = await db.get(originalId)
|
||||||
|
await db.remove(originalDoc._id, originalDoc._rev)
|
||||||
|
}
|
||||||
|
if (old && old._rev) {
|
||||||
|
viewDoc._rev = old._rev
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// didn't exist, just skip
|
||||||
|
}
|
||||||
|
await db.put(viewDoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteView(db, viewName) {
|
||||||
|
if (env.SELF_HOSTED) {
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
const view = designDoc.views[viewName]
|
||||||
|
delete designDoc.views[viewName]
|
||||||
|
await db.put(designDoc)
|
||||||
|
return view
|
||||||
|
} else {
|
||||||
|
const id = generateMemoryViewID(viewName)
|
||||||
|
const viewDoc = await db.get(id)
|
||||||
|
await db.remove(viewDoc._id, viewDoc._rev)
|
||||||
|
return viewDoc.view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = {
|
||||||
|
fetch: async ctx => {
|
||||||
|
const db = new CouchDB(ctx.appId)
|
||||||
|
ctx.body = await getViews(db)
|
||||||
},
|
},
|
||||||
save: async ctx => {
|
save: async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
const { originalName, ...viewToSave } = ctx.request.body
|
const { originalName, ...viewToSave } = ctx.request.body
|
||||||
const designDoc = await db.get("_design/database")
|
|
||||||
const view = viewTemplate(viewToSave)
|
const view = viewTemplate(viewToSave)
|
||||||
|
|
||||||
if (!viewToSave.name) {
|
if (!viewToSave.name) {
|
||||||
ctx.throw(400, "Cannot create view without a name")
|
ctx.throw(400, "Cannot create view without a name")
|
||||||
}
|
}
|
||||||
|
|
||||||
designDoc.views = {
|
await saveView(db, originalName, viewToSave, view)
|
||||||
...designDoc.views,
|
|
||||||
[viewToSave.name]: view,
|
|
||||||
}
|
|
||||||
|
|
||||||
// view has been renamed
|
|
||||||
if (originalName) {
|
|
||||||
delete designDoc.views[originalName]
|
|
||||||
}
|
|
||||||
|
|
||||||
await db.put(designDoc)
|
|
||||||
|
|
||||||
// add views to table document
|
// add views to table document
|
||||||
const table = await db.get(ctx.request.body.tableId)
|
const table = await db.get(ctx.request.body.tableId)
|
||||||
|
@ -53,11 +127,9 @@ const controller = {
|
||||||
view.meta.schema = table.schema
|
view.meta.schema = table.schema
|
||||||
}
|
}
|
||||||
table.views[viewToSave.name] = view.meta
|
table.views[viewToSave.name] = view.meta
|
||||||
|
|
||||||
if (originalName) {
|
if (originalName) {
|
||||||
delete table.views[originalName]
|
delete table.views[originalName]
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
@ -67,13 +139,8 @@ const controller = {
|
||||||
},
|
},
|
||||||
destroy: async ctx => {
|
destroy: async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
const designDoc = await db.get("_design/database")
|
|
||||||
const viewName = decodeURI(ctx.params.viewName)
|
const viewName = decodeURI(ctx.params.viewName)
|
||||||
const view = designDoc.views[viewName]
|
const view = await deleteView(db, viewName)
|
||||||
delete designDoc.views[viewName]
|
|
||||||
|
|
||||||
await db.put(designDoc)
|
|
||||||
|
|
||||||
const table = await db.get(view.meta.tableId)
|
const table = await db.get(view.meta.tableId)
|
||||||
delete table.views[viewName]
|
delete table.views[viewName]
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
|
@ -82,10 +149,9 @@ const controller = {
|
||||||
},
|
},
|
||||||
exportView: async ctx => {
|
exportView: async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
const designDoc = await db.get("_design/database")
|
|
||||||
const viewName = decodeURI(ctx.query.view)
|
const viewName = decodeURI(ctx.query.view)
|
||||||
|
const view = await getView(db, viewName)
|
||||||
|
|
||||||
const view = designDoc.views[viewName]
|
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
if (!format) {
|
if (!format) {
|
||||||
ctx.throw(400, "Format must be specified, either csv or json")
|
ctx.throw(400, "Format must be specified, either csv or json")
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
const PouchDB = require("pouchdb")
|
||||||
|
const memory = require("pouchdb-adapter-memory")
|
||||||
|
|
||||||
|
PouchDB.plugin(memory)
|
||||||
|
const Pouch = PouchDB.defaults({
|
||||||
|
prefix: undefined,
|
||||||
|
adapter: "memory",
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.runView = async (appId, view, calculation, group, data) => {
|
||||||
|
// appId doesn't really do anything since its all in memory
|
||||||
|
// use it just incase multiple databases at the same time
|
||||||
|
const db = new Pouch(appId)
|
||||||
|
await db.put({
|
||||||
|
_id: "_design/database",
|
||||||
|
views: {
|
||||||
|
runner: view,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// write all the docs to the in memory Pouch
|
||||||
|
await db.bulkDocs(data)
|
||||||
|
const response = await db.query(`database/runner`, {
|
||||||
|
include_docs: !calculation,
|
||||||
|
group: !!group,
|
||||||
|
})
|
||||||
|
// need to fix the revs to be totally accurate
|
||||||
|
for (let row of response.rows) {
|
||||||
|
if (!row._rev || !row._id) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const found = data.find(possible => possible._id === row._id)
|
||||||
|
if (found) {
|
||||||
|
row._rev = found._rev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db.destroy()
|
||||||
|
return response
|
||||||
|
}
|
|
@ -39,6 +39,7 @@ const DocumentTypes = {
|
||||||
QUERY: "query",
|
QUERY: "query",
|
||||||
DEPLOYMENTS: "deployments",
|
DEPLOYMENTS: "deployments",
|
||||||
METADATA: "metadata",
|
METADATA: "metadata",
|
||||||
|
MEM_VIEW: "view",
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewNames = {
|
const ViewNames = {
|
||||||
|
@ -348,6 +349,14 @@ exports.getMetadataParams = (type, entityId = null, otherProps = {}) => {
|
||||||
return getDocParams(DocumentTypes.METADATA, docId, otherProps)
|
return getDocParams(DocumentTypes.METADATA, docId, otherProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.generateMemoryViewID = viewName => {
|
||||||
|
return `${DocumentTypes.MEM_VIEW}${SEPARATOR}${viewName}`
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getMemoryViewParams = (otherProps = {}) => {
|
||||||
|
return getDocParams(DocumentTypes.MEM_VIEW, null, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This can be used with the db.allDocs to get a list of IDs
|
* This can be used with the db.allDocs to get a list of IDs
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -66,3 +66,10 @@ module.exports = {
|
||||||
return !isDev()
|
return !isDev()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert any strings to numbers if required, like "0" would be true otherwise
|
||||||
|
for (let [key, value] of Object.entries(module.exports)) {
|
||||||
|
if (typeof value === "string" && !isNaN(parseInt(value))) {
|
||||||
|
module.exports[key] = parseInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue