Big Refactor of all SQL calls to comply with database changes

Added tag suggestions when entering tag field
Cleaned up animations to make them REAL smooth
This commit is contained in:
Max G 2019-07-30 20:27:26 +00:00
parent 0d86aa4ff9
commit b2dc6e5218
8 changed files with 111 additions and 77 deletions

View File

@ -232,7 +232,7 @@
setTimeout(() => { setTimeout(() => {
resolve(true) resolve(true)
return return
}, 500) }, 300)
} }
const postData = { const postData = {
@ -256,7 +256,7 @@
vm.lastNoteHash = vm.hashString(vm.noteText) vm.lastNoteHash = vm.hashString(vm.noteText)
resolve(true) resolve(true)
}) })
}, 500) }, 300)
}) })
}, },
@ -335,7 +335,7 @@
} }
.size-down { .size-down {
animation: size-down 0.5s linear both; animation: size-down 0.5s ease;
} }
@keyframes size-down { @keyframes size-down {
@ -345,7 +345,7 @@
} }
100% { 100% {
opacity: 0; opacity: 0;
top: 405vh; top: 30vh;
} }
} }

View File

@ -4,10 +4,13 @@
{{ucWords(tag.text)}} <i class="delete icon" v-on:click="removeTag(tag.id)"></i> {{ucWords(tag.text)}} <i class="delete icon" v-on:click="removeTag(tag.id)"></i>
</div> </div>
<div class="ui form"> <div class="ui form">
<input v-model="newTagInput" placeholder="Add Tag" <input
placeholder="Add Tag"
v-model="newTagInput"
v-on:keydown="tagInput" v-on:keydown="tagInput"
v-on:keyup="onKeyup" v-on:keyup="onKeyup"
v-on:blur="onBlur" v-on:blur="onBlur"
v-on:focus="onFocus"
/> />
<div class="suggestion-box" v-if="suggestions.length > 0"> <div class="suggestion-box" v-if="suggestions.length > 0">
<div class="suggestion-item" v-for="(item, index) in suggestions" :class="{ 'active':(index == selection) }" v-on:click="onClickTag(index)"> <div class="suggestion-item" v-for="(item, index) in suggestions" :class="{ 'active':(index == selection) }" v-on:click="onClickTag(index)">
@ -70,7 +73,6 @@
if(this.selection >= this.suggestions.length){ if(this.selection >= this.suggestions.length){
this.selection = -1 //No selection made this.selection = -1 //No selection made
} }
console.log('Current Selection Index: ', this.selection)
return return
} }
//Down - 40 - Go Down Suggestion //Down - 40 - Go Down Suggestion
@ -79,7 +81,6 @@
if(this.selection < -1){ if(this.selection < -1){
this.selection = this.suggestions.length -1 //No selection made this.selection = this.suggestions.length -1 //No selection made
} }
console.log('Current Selection Index: ', this.selection)
return; return;
} }
@ -137,7 +138,22 @@
vm.getTags() vm.getTags()
}) })
}, },
onFocus(){
console.log('Focused on tag edit')
//Show suggested tags
let vm = this
let postData = {
'noteId':this.noteId
}
axios.post('/api/tag/latest', postData)
.then(response => {
vm.suggestions = response.data
vm.selection = -1 //Nothing selected
})
},
onKeyup(){ onKeyup(){
//Clear tags if backspaced
if(this.newTagInput == ''){ if(this.newTagInput == ''){
this.clearSuggestions() this.clearSuggestions()
} }

View File

@ -28,22 +28,22 @@
<delete-button :note-id="note.id" /> <delete-button :note-id="note.id" />
</div> </div>
</div> </div>
</div>
<!-- Display highlights from solr results -->
<div v-if="note.note_highlights.length > 0" class="term-usage">
<p><i class="paragraph icon"></i> Note Text</p>
<div class="usage-row" v-for="highlight in note.note_highlights" v-html="highlight"></div>
</div>
<div v-if="note.attachment_highlights.length > 0" class="term-usage">
<p><i class="linkify icon"></i> Note URL Text</p>
<div class="usage-row" v-for="highlight in note.attachment_highlights" v-html="highlight"></div>
</div>
<div v-if="note.tag_highlights.length > 0" class="term-usage">
<i class="tags icon"></i> Tag
<div class="ui icon large label" v-for="highlight in note.tag_highlights" v-html="highlight"></div>
</div>
<!-- Display highlights from solr results --> </div>
<div v-if="note.note_highlights.length > 0" class="term-usage">
<p>Note Text</p>
<div class="usage-row" v-for="highlight in note.note_highlights" v-html="highlight"></div>
</div>
<div v-if="note.attachment_highlights.length > 0" class="term-usage">
<p><i class="linkify icon"></i> Note URL Text</p>
<div class="usage-row" v-for="highlight in note.attachment_highlights" v-html="highlight"></div>
</div>
<div v-if="note.tag_highlights.length > 0" class="term-usage">
<i class="tags icon"></i> Tag
<div class="ui icon large label" v-for="highlight in note.tag_highlights" v-html="highlight"></div>
</div>
</div> </div>
</template> </template>
@ -77,8 +77,9 @@
<style type="text/css"> <style type="text/css">
.term-usage { .term-usage {
border: 1px solid #DDD; border-top: 1px solid #DDD;
padding: 10px; padding: 10px;
width: 100%;
} }
.term-usage em { .term-usage em {
color: green; color: green;

View File

@ -77,7 +77,7 @@
v-for="note in notes" v-for="note in notes"
:onClick="openNote" :onClick="openNote"
:data="note" :data="note"
:key="note.id + note.color + searchTerm + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length" :key="note.id + note.color + note.note_highlights.length + note.attachment_highlights.length + ' -' + note.tag_highlights.length + '-' +note.title.length + '-' +note.subtext.length"
/> />
</div> </div>

View File

@ -24,7 +24,7 @@ Note.create = (userId, noteText) => {
const created = Math.round((+new Date)/1000) const created = Math.round((+new Date)/1000)
db.promise() db.promise()
.query('INSERT INTO notes (user, text, created) VALUES (?,?,?)', [userId, noteText, created]) .query('INSERT INTO note (user_id, text, created) VALUES (?,?,?)', [userId, noteText, created])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0].insertId) //Only return the new note ID when creating a new note resolve(rows[0].insertId) //Only return the new note ID when creating a new note
}) })
@ -38,7 +38,7 @@ Note.update = (userId, noteId, noteText, fancyInput, color) => {
const now = Math.round((+new Date)/1000) const now = Math.round((+new Date)/1000)
db.promise() db.promise()
.query('UPDATE notes SET text = ?, raw_input = ?, updated = ?, color = ? WHERE id = ? AND user = ? LIMIT 1', [noteText, fancyInput, now, color, noteId, userId]) .query('UPDATE note SET text = ?, raw_input = ?, updated = ?, color = ? WHERE id = ? AND user_id = ? LIMIT 1', [noteText, fancyInput, now, color, noteId, userId])
.then((rows, fields) => { .then((rows, fields) => {
//Process note text and attachment data //Process note text and attachment data
@ -52,7 +52,7 @@ Note.update = (userId, noteId, noteText, fancyInput, color) => {
'id': noteId,//string - ID of note 'id': noteId,//string - ID of note
'user_id': userId,//int 'user_id': userId,//int
'note_text': noteText, 'note_text': noteText,
'notes_tags': tagString, 'note_tag': tagString,
'attachment_text': attachmentText, 'attachment_text': attachmentText,
}; };
// Update document to Solr server // Update document to Solr server
@ -75,16 +75,12 @@ Note.update = (userId, noteId, noteText, fancyInput, color) => {
Note.delete = (userId, noteId) => { Note.delete = (userId, noteId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// DELETE FROM notes WHERE notes.id = 290 AND notes.user = 61; db.promise().query('DELETE FROM note WHERE note.id = ? AND note.user_id = ?', [noteId,userId])
// DELETE FROM attachment WHERE attachment.note_id = 290 AND attachment.user_id = 61;
// DELETE FROM notes_tags WHERE notes_tags.note_id = 290 AND notes_tags.user_id = 61;
db.promise().query('DELETE FROM notes WHERE notes.id = ? AND notes.user = ?', [noteId,userId])
.then((rows, fields) => { .then((rows, fields) => {
db.promise().query('DELETE FROM attachment WHERE attachment.note_id = ? AND attachment.user_id = ?', [noteId,userId]) db.promise().query('DELETE FROM attachment WHERE attachment.note_id = ? AND attachment.user_id = ?', [noteId,userId])
.then((rows, fields)=> { .then((rows, fields)=> {
db.promise().query('DELETE FROM notes_tags WHERE notes_tags.note_id = ? AND notes_tags.user_id = ?', [noteId,userId]) db.promise().query('DELETE FROM note_tag WHERE note_tag.note_id = ? AND note_tag.user_id = ?', [noteId,userId])
.then((rows, fields)=> { .then((rows, fields)=> {
console.log('All Deleted')
resolve(true) resolve(true)
}) })
}) })
@ -95,7 +91,7 @@ Note.delete = (userId, noteId) => {
Note.get = (userId, noteId) => { Note.get = (userId, noteId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query('SELECT text, updated, raw_input, color FROM notes WHERE user = ? AND id = ? LIMIT 1', [userId,noteId]) .query('SELECT text, updated, raw_input, color FROM note WHERE user_id = ? AND id = ? LIMIT 1', [userId,noteId])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0][0]) resolve(rows[0][0])
}) })
@ -103,29 +99,18 @@ Note.get = (userId, noteId) => {
}) })
} }
Note.getLatest = (userId) => {
return new Promise((resolve, reject) => {
db.promise()
.query('SELECT id, SUBSTRING(text, 1, 100) as text FROM notes WHERE user = ? ORDER BY updated DESC, created DESC', [userId])
.then((rows, fields) => {
resolve(rows[0])
})
.catch(console.log)
})
}
Note.solrQuery = (userId, searchQuery, searchTags) => { Note.solrQuery = (userId, searchQuery, searchTags) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(searchQuery != '' && searchQuery != null){ if(searchQuery != '' && searchQuery != null){
let urlQuery = `/solr/note/select?hl.fl=note_text&hl=on&q=user_id:${userId} AND note_text:${searchQuery}&wt=json` let urlQuery = `/solr/note/select?hl.fl=note_text&hl=on&q=user_id:${userId} AND note_text:${searchQuery}&wt=json`
urlQuery = `/solr/note/select? urlQuery = `/solr/note/select?
hl.fl=note_text,attachment_text,notes_tags& hl.fl=note_text,attachment_text,note_tag&
hl=on& hl=on&
q=user_id:${userId} AND (note_text:${searchQuery} OR attachment_text:${searchQuery} OR notes_tags:${searchQuery})& q=user_id:${userId} AND (note_text:${searchQuery} OR attachment_text:${searchQuery} OR note_tag:${searchQuery})&
wt=json& wt=json&
fl=id& fl=id&
hl.fl=note_text,attachment_text,notes_tags& hl.fl=note_text,attachment_text,note_tag&
hl.snippets=20& hl.snippets=20&
hl.maxAnalyzedChars=100000` hl.maxAnalyzedChars=100000`
@ -166,16 +151,20 @@ Note.search = (userId, searchQuery, searchTags) => {
//Default note lookup gets all notes //Default note lookup gets all notes
let noteSearchQuery = ` let noteSearchQuery = `
SELECT notes.id, SUBSTRING(notes.text, 1, 400) as text, updated, color, count(distinct notes_tags.id) as tag_count, count(distinct attachment.id) as attachment_count SELECT note.id,
FROM notes SUBSTRING(note.text, 1, 400) as text,
LEFT JOIN notes_tags ON (notes.id = notes_tags.note_id) updated, color,
LEFT JOIN attachment ON (notes.id = attachment.note_id AND attachment.attachment_type = 1) count(distinct note_tag.id) as tag_count,
WHERE user = ?` count(distinct attachment.id) as attachment_count
FROM note
LEFT JOIN note_tag ON (note.id = note_tag.note_id)
LEFT JOIN attachment ON (note.id = attachment.note_id AND attachment.attachment_type = 1)
WHERE note.user_id = ?`
let searchParams = [userId] let searchParams = [userId]
if(solrNoteIds.length > 0){ if(solrNoteIds.length > 0){
searchParams.push(solrNoteIds) searchParams.push(solrNoteIds)
noteSearchQuery += ' AND notes.id IN (?)' noteSearchQuery += ' AND note.id IN (?)'
} }
// if(searchQuery != ''){ // if(searchQuery != ''){
@ -186,11 +175,11 @@ Note.search = (userId, searchQuery, searchTags) => {
if(searchTags.length > 0){ if(searchTags.length > 0){
//If tags are passed, use those tags in search //If tags are passed, use those tags in search
searchParams.push(searchTags) searchParams.push(searchTags)
noteSearchQuery += ' AND notes_tags.tag_id IN (?)' noteSearchQuery += ' AND note_tag.tag_id IN (?)'
} }
//Finish up note query //Finish up note query
noteSearchQuery += ' GROUP BY notes.id ORDER BY updated DESC, created DESC, id DESC' noteSearchQuery += ' GROUP BY note.id ORDER BY updated DESC, created DESC, id DESC'
db.promise() db.promise()
.query(noteSearchQuery, searchParams) .query(noteSearchQuery, searchParams)
@ -243,8 +232,8 @@ Note.search = (userId, searchQuery, searchTags) => {
if(highlights && highlights[note.id] && highlights[note.id].attachment_text){ if(highlights && highlights[note.id] && highlights[note.id].attachment_text){
note['attachment_highlights'] = highlights[note.id].attachment_text note['attachment_highlights'] = highlights[note.id].attachment_text
} }
if(highlights && highlights[note.id] && highlights[note.id].notes_tags){ if(highlights && highlights[note.id] && highlights[note.id].note_tag){
note['tag_highlights'] = highlights[note.id].notes_tags note['tag_highlights'] = highlights[note.id].note_tag
} }
//Clear out note.text before sending it to front end //Clear out note.text before sending it to front end
@ -258,11 +247,11 @@ Note.search = (userId, searchQuery, searchTags) => {
//Only show tags of selected notes //Only show tags of selected notes
db.promise() db.promise()
.query(`SELECT tags.id, tags.text, count(tags.id) as usages FROM notes_tags .query(`SELECT tag.id, tag.text, count(tag.id) as usages FROM note_tag
JOIN tags ON (tags.id = notes_tags.tag_id) JOIN tag ON (tag.id = note_tag.tag_id)
WHERE notes_tags.user_id = ? WHERE note_tag.user_id = ?
AND note_id IN (?) AND note_id IN (?)
GROUP BY tags.id GROUP BY tag.id
ORDER BY usages DESC;`,[userId, noteIds]) ORDER BY usages DESC;`,[userId, noteIds])
.then((tagRows, tagFields) => { .then((tagRows, tagFields) => {

View File

@ -6,7 +6,7 @@ let Tag = module.exports = {}
Tag.removeTagFromNote = (userId, tagId) => { Tag.removeTagFromNote = (userId, tagId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query(`DELETE FROM notes_tags WHERE id = ? AND user_id = ? LIMIT 1;`, [tagId, userId]) .query(`DELETE FROM note_tag WHERE id = ? AND user_id = ? LIMIT 1;`, [tagId, userId])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0]) //Return new ID resolve(rows[0]) //Return new ID
}) })
@ -50,7 +50,7 @@ Tag.associateWithNote = (userId, noteId, tagId) => {
//Check if tag already exists on note before adding note //Check if tag already exists on note before adding note
db.promise() db.promise()
.query(`SELECT * FROM notes_tags WHERE note_id = ? AND tag_id = ? AND user_id = ?;`, [noteId, tagId, userId]) .query(`SELECT * FROM note_tag WHERE note_id = ? AND tag_id = ? AND user_id = ?;`, [noteId, tagId, userId])
.then((rows, fields) => { .then((rows, fields) => {
//If matching tag does not exist on note //If matching tag does not exist on note
@ -58,7 +58,7 @@ Tag.associateWithNote = (userId, noteId, tagId) => {
//Add tag to note //Add tag to note
db.promise() db.promise()
.query(`INSERT INTO notes_tags (note_id, tag_id, user_id) VALUES (?,?,?);`, [noteId, tagId, userId]) .query(`INSERT INTO note_tag (note_id, tag_id, user_id) VALUES (?,?,?);`, [noteId, tagId, userId])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0]) resolve(rows[0])
}) })
@ -75,7 +75,7 @@ Tag.associateWithNote = (userId, noteId, tagId) => {
Tag.add = (tagText) => { Tag.add = (tagText) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query(`INSERT INTO tags (text, hash) VALUES (?,?);`, [tagText,0]) .query(`INSERT INTO tag (text, hash) VALUES (?,?);`, [tagText,0])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0].insertId) //Return new ID resolve(rows[0].insertId) //Return new ID
}) })
@ -86,8 +86,8 @@ Tag.add = (tagText) => {
Tag.get = (userId, noteId) => { Tag.get = (userId, noteId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query(`SELECT notes_tags.id, tags.text FROM notes_tags .query(`SELECT note_tag.id, tag.text FROM note_tag
JOIN tags ON (tags.id = notes_tags.tag_id) JOIN tag ON (tag.id = note_tag.tag_id)
WHERE user_id = ? AND note_id = ?;`, [userId, noteId]) WHERE user_id = ? AND note_id = ?;`, [userId, noteId])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0]) //Return all tags found by query resolve(rows[0]) //Return all tags found by query
@ -115,7 +115,7 @@ Tag.string = (userId, noteId) => {
Tag.lookup = (tagText) => { Tag.lookup = (tagText) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query(`SELECT * FROM tags WHERE text = ?;`, [tagText]) .query(`SELECT * FROM tag WHERE text = ?;`, [tagText])
.then((rows, fields) => { .then((rows, fields) => {
resolve(rows[0]) //Return all tags found by query resolve(rows[0]) //Return all tags found by query
}) })
@ -130,12 +130,12 @@ Tag.suggest = (userId, noteId, tagText) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query(`SELECT text FROM notes_tags .query(`SELECT text FROM note_tag
JOIN tags ON notes_tags.tag_id = tags.id JOIN tag ON note_tag.tag_id = tag.id
WHERE notes_tags.user_id = ? WHERE note_tag.user_id = ?
AND tags.text LIKE ? AND tag.text LIKE ?
AND notes_tags.tag_id NOT IN ( AND note_tag.tag_id NOT IN (
SELECT notes_tags.tag_id FROM notes_tags WHERE notes_tags.note_id = ? SELECT note_tag.tag_id FROM note_tag WHERE note_tag.note_id = ?
) )
GROUP BY text GROUP BY text
LIMIT 6;`, [userId, tagText, noteId]) LIMIT 6;`, [userId, tagText, noteId])
@ -144,4 +144,26 @@ Tag.suggest = (userId, noteId, tagText) => {
}) })
.catch(console.log) .catch(console.log)
}) })
}
//Suggest note tags - don't suggest tags already on note
Tag.latest = (userId, noteId) => {
return new Promise((resolve, reject) => {
db.promise()
.query(`SELECT tag.text FROM note_tag
JOIN tag ON note_tag.tag_id = tag.id
JOIN note ON note_tag.note_id = note.id
WHERE note_tag.user_id = ?
AND note_tag.tag_id NOT IN (
SELECT note_tag.tag_id FROM note_tag WHERE note_tag.note_id = ?
)
GROUP BY tag.text, note.updated
ORDER BY note.updated DESC
LIMIT 6;`, [userId, noteId])
.then((rows, fields) => {
resolve(rows[0]) //Return new ID
})
.catch(console.log)
})
} }

View File

@ -13,7 +13,7 @@ User.login = (username, password) => {
const lowerName = username.toLowerCase(); const lowerName = username.toLowerCase();
db.promise() db.promise()
.query('SELECT * FROM users WHERE username = ? LIMIT 1', [lowerName]) .query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
.then((rows, fields) => { .then((rows, fields) => {
//Pull out user data from database results //Pull out user data from database results
@ -58,7 +58,7 @@ User.create = (username, password) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.promise() db.promise()
.query('SELECT * FROM users WHERE username = ? LIMIT 1', [lowerName]) .query('SELECT * FROM user WHERE username = ? LIMIT 1', [lowerName])
.then((rows, fields) => { .then((rows, fields) => {
if(rows[0].length === 0){ //No users returned, create new one. Start with hashing password if(rows[0].length === 0){ //No users returned, create new one. Start with hashing password
@ -88,7 +88,7 @@ User.create = (username, password) => {
}; };
db.promise() db.promise()
.query('INSERT INTO users SET ?', new_user) .query('INSERT INTO user SET ?', new_user)
.then((rows, fields) => { .then((rows, fields) => {
if(rows[0].affectedRows == 1){ if(rows[0].affectedRows == 1){

View File

@ -19,6 +19,12 @@ router.post('/suggest', function (req, res) {
.then( data => res.send(data) ) .then( data => res.send(data) )
}) })
//Get latest tags based on when it was put on a note
router.post('/latest', function (req, res) {
Tags.latest(userId, req.body.noteId)
.then( data => res.send(data) )
})
//Get the latest notes the user has created //Get the latest notes the user has created
router.post('/addtonote', function (req, res) { router.post('/addtonote', function (req, res) {
Tags.addToNote(userId, req.body.noteId, req.body.tagText.toLowerCase()) Tags.addToNote(userId, req.body.noteId, req.body.tagText.toLowerCase())