src/store/modules/document.js
import { compact, concat, findIndex, flattenDeep, get, keys, map, sortBy, sumBy, uniqBy, values } from 'lodash'
import Vue from 'vue'
import EsDocList from '@/api/resources/EsDocList'
export function initialState() {
return {
doc: null,
idAndRouting: null,
isContentLoaded: false,
isTranslatedContentLoaded: false,
isLoadingNamedEntities: false,
isRecommended: false,
namedEntitiesPaginatedByCategories: {
PERSON: [],
ORGANIZATION: [],
LOCATION: [],
EMAIL: []
},
parentDocument: null,
recommendedBy: [],
rootDocument: null,
useContentTextLazyLoading: false,
showTranslatedContent: true,
tags: []
}
}
export const state = initialState()
export const getters = {
countNamedEntitiesInCategory(state) {
return (category) => {
const pages = get(state, ['namedEntitiesPaginatedByCategories', category], [])
// Sum up all page size
return sumBy(pages, (page) => get(page, 'hits.length', 0))
}
},
namedEntities(state) {
const categoriesPages = values(state.namedEntitiesPaginatedByCategories)
const hits = categoriesPages.map((pages) => pages.map((page) => page.hits))
return flattenDeep(hits)
},
categories(state) {
return keys(state.namedEntitiesPaginatedByCategories)
}
}
export const mutations = {
reset(state) {
const s = initialState()
const persistedFields = ['showTranslatedContent']
persistedFields.forEach((key) => delete s[key])
Object.keys(s).forEach((key) => {
state[key] = s[key]
})
},
idAndRouting(state, idAndRouting) {
mutations.reset(state)
Vue.set(state, 'idAndRouting', idAndRouting)
},
doc(state, raw) {
if (raw !== null) {
Vue.set(state, 'doc', EsDocList.instantiate(raw))
Vue.set(state, 'isContentLoaded', state.doc.hasContent)
Vue.set(state, 'isTranslatedContentLoaded', state.doc.hasTranslatedContent)
Vue.set(state, 'useContentTextLazyLoading', state.doc.hasBigContentTextLength)
} else {
Vue.set(state, 'doc', null)
}
},
content(state, content = null) {
if (state.doc) {
Vue.set(state.doc, 'content', content)
Vue.set(state, 'isContentLoaded', true)
}
},
translations(state, translations = []) {
if (state.doc) {
Vue.set(state.doc, 'translations', translations)
Vue.set(state, 'isTranslatedContentLoaded', true)
}
},
tags(state, tags = []) {
Vue.set(state, 'tags', tags)
},
namedEntities(state, raw) {
Vue.set(state, 'namedEntities', new EsDocList(raw).hits)
},
namedEntitiesPageInCategory(state, { category, page }) {
if (state.namedEntitiesPaginatedByCategories[category]) {
state.namedEntitiesPaginatedByCategories[category].push(page)
}
},
namedEntitiesPagesInCategory(state, { category, pages = [] } = {}) {
state.namedEntitiesPaginatedByCategories[category] = pages
},
parentDocument(state, raw) {
if (raw !== null) {
Vue.set(state, 'parentDocument', EsDocList.instantiate(raw))
state.doc.parent = raw
} else {
Vue.set(state, 'parentDocument', null)
}
return state.parentDocument
},
rootDocument(state, raw) {
if (raw !== null) {
Vue.set(state, 'rootDocument', EsDocList.instantiate(raw))
state.doc.root = raw
} else {
Vue.set(state, 'rootDocument', null)
}
return state.rootDocument
},
toggleShowTranslatedContent(state, toggle = null) {
Vue.set(state, 'showTranslatedContent', toggle !== null ? toggle : !state.showTranslatedContent)
},
addTag(state, { tag, userId }) {
const tags = map(compact(tag.split(' ')), (tag) => {
return { label: tag, user: { id: userId }, creationDate: Date.now() }
})
Vue.set(state, 'tags', uniqBy(concat(state.tags, tags), 'label'))
},
deleteTag(state, tagToDelete) {
state.tags.splice(findIndex(state.tags, { label: tagToDelete.label }), 1)
},
isRecommended(state, isRecommended) {
Vue.set(state, 'isRecommended', isRecommended)
},
recommendedBy(state, recommendedBy = []) {
Vue.set(state, 'recommendedBy', recommendedBy)
},
markAsRecommended(state, userId) {
state.recommendedBy.push(userId)
},
unmarkAsRecommended(state, userId) {
const index = state.recommendedBy.indexOf(userId)
if (index > -1) {
Vue.delete(state.recommendedBy, index)
}
}
}
function actionBuilder(api) {
return {
async get({ commit, state }, idAndRouting) {
try {
const { id, index, routing } = idAndRouting
const doc = await api.elasticsearch.getDocumentWithoutContent(index, id, routing)
commit('idAndRouting', idAndRouting)
commit('doc', doc)
} catch (_) {
commit('doc', null)
}
return state.doc
},
async getContent({ commit, state }) {
if (state.doc !== null) {
const { id, routing } = state.idAndRouting
const { index } = state.doc
const doc = await api.elasticsearch.getDocumentWithContent(index, id, routing)
const translations = get(doc, '_source.content_translated')
const content = get(doc, '_source.content')
commit('translations', translations)
commit('content', content)
return content
}
},
getContentSlice({ state }, { offset, limit, targetLanguage }) {
if (state.doc !== null) {
const { id, routing } = state.idAndRouting
const { index } = state.doc
const o = offset ?? 0
const l = limit ?? 0
return api.getDocumentSlice(index, id, o, l, targetLanguage, routing)
}
},
async setContent({ commit, state }, content) {
if (state.doc !== null) {
commit('content', content)
}
},
async getContentMaxOffset({ state }, { targetLanguage }) {
if (state.doc !== null) {
const { id, routing } = state.idAndRouting
const { index } = state.doc
const slice = await api.getDocumentSlice(index, id, 0, 0, targetLanguage, routing)
return slice.maxOffset
}
},
async searchOccurrences({ state }, { query, targetLanguage }) {
if (state.doc !== null) {
const { id, routing } = state.idAndRouting
const { index } = state.doc
return api.searchDocument(index, id, query, targetLanguage, routing)
}
return { count: 0, offsets: [] }
},
async getParent({ commit, state }) {
if (state.doc !== null && state.doc.raw._source.extractionLevel > 0) {
try {
const { index } = state.doc
const { parentDocument: id, rootDocument: routing } = state.doc.raw._source
const doc = await api.elasticsearch.getDocumentWithoutContent(index, id, routing)
commit('parentDocument', doc)
} catch (_) {
commit('parentDocument', null)
}
}
return state.parentDocument
},
async getRoot({ commit, state }) {
if (state.doc !== null && state.doc.raw._source.extractionLevel > 0) {
try {
const { index } = state.doc
const { rootDocument: id } = state.doc.raw._source
const doc = await api.elasticsearch.getDocumentWithoutContent(index, id, id)
commit('rootDocument', doc)
} catch (_) {
commit('rootDocument', null)
}
}
return state.rootDocument
},
getFirstPageForNamedEntityInCategory({ dispatch, commit }, { category, filterToken = null } = {}) {
commit('namedEntitiesPagesInCategory', { category, pages: [] })
return dispatch('getNextPageForNamedEntityInCategory', { category, filterToken })
},
async getFirstPageForNamedEntityInAllCategories({ dispatch, getters }, { filterToken = null } = {}) {
for (const category of getters.categories) {
await dispatch('getFirstPageForNamedEntityInCategory', { filterToken, category })
}
},
async getNextPageForNamedEntityInCategory({ state, getters, commit }, { category, filterToken = null } = {}) {
try {
const from = getters.countNamedEntitiesInCategory(category)
const index = state.doc.index
const { id, routing } = state.doc
const raw = await api.elasticsearch.getDocumentNamedEntitiesInCategory(
index,
id,
routing,
from,
50,
category,
filterToken
)
const page = new EsDocList(raw)
if (from === 0) {
const pages = [page]
return commit('namedEntitiesPagesInCategory', { category, pages })
} else {
return commit('namedEntitiesPageInCategory', { category, page })
}
} catch (_) {
return null
}
},
async getTags({ state, commit }) {
try {
const tags = await api.getTags(state.doc.index, state.doc.id)
commit('tags', tags)
} catch (_) {
commit('tags')
}
return state.tags
},
async tag({ state, dispatch }, { documents, tag, userId }) {
const index = state.doc ? state.doc.index : get(documents, '0.index', null)
await api.tagDocuments(index, map(documents, 'id'), compact(tag.split(' ')))
if (documents.length === 1) dispatch('addTag', { tag, userId })
},
addTag({ state, commit }, { tag, userId }) {
commit('addTag', { tag, userId })
},
async deleteTag({ state, commit }, { documents, tag }) {
await api.untagDocuments(state.doc.index, map(documents, 'id'), [tag.label])
if (documents.length === 1) commit('deleteTag', tag)
},
async toggleAsRecommended({ state, commit }, userId) {
if (state.isRecommended) {
await api.setUnmarkAsRecommended(state.doc.index, [state.doc.id])
commit('unmarkAsRecommended', userId)
commit('isRecommended', false)
} else {
await api.setMarkAsRecommended(state.doc.index, [state.doc.id])
commit('markAsRecommended', userId)
commit('isRecommended', true)
}
},
async getRecommendationsByDocuments({ state, commit }, userId) {
try {
const recommendedBy = await api.getRecommendationsByDocuments(state.doc.index, state.doc.id)
commit('recommendedBy', map(sortBy(get(recommendedBy, 'aggregates', []), 'item.id'), 'item.id'))
const index = state.recommendedBy.indexOf(userId)
if (index > -1) {
commit('isRecommended', true)
}
} catch (_) {
commit('recommendedBy', [])
}
return state.recommendedBy
}
}
}
export const documentStoreBuilder = function (api) {
const actions = actionBuilder(api)
return {
namespaced: true,
getters,
state,
mutations,
actions
}
}