| name | evernote-core-workflow-b |
| description | Execute Evernote secondary workflow: Search and Retrieval.
Use when implementing search features, finding notes,
filtering content, or building search interfaces.
Trigger with phrases like "search evernote", "find evernote notes",
"evernote search", "query evernote".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Evernote Core Workflow B: Search & Retrieval
Overview
Comprehensive search and retrieval workflow for Evernote, including search grammar, filters, pagination, and related notes discovery.
Prerequisites
- Completed
evernote-install-auth setup
- Understanding of Evernote search grammar
- Valid access token configured
Instructions
Step 1: Search Service Foundation
const Evernote = require('evernote');
class SearchService {
constructor(noteStore) {
this.noteStore = noteStore;
}
async search(query, options = {}) {
const {
maxResults = 50,
offset = 0,
sortOrder = 'UPDATED',
ascending = false
} = options;
const filter = new Evernote.NoteStore.NoteFilter({
words: query,
ascending,
order: Evernote.Types.NoteSortOrder[sortOrder]
});
const spec = new Evernote.NoteStore.NotesMetadataResultSpec({
includeTitle: true,
includeContentLength: true,
includeCreated: true,
includeUpdated: true,
includeTagGuids: true,
includeNotebookGuid: true,
includeAttributes: true
});
const result = await this.noteStore.findNotesMetadata(
filter,
offset,
maxResults,
spec
);
return {
notes: result.notes,
totalNotes: result.totalNotes,
hasMore: offset + result.notes.length < result.totalNotes
};
}
async searchInNotebook(notebookGuid, query, maxResults = 50) {
const filter = new Evernote.NoteStore.NoteFilter({
notebookGuid,
words: query
});
const spec = this.getDefaultResultSpec();
return this.noteStore.findNotesMetadata(filter, 0, maxResults, spec);
}
async searchByTags(tagGuids, maxResults = 50) {
const filter = new Evernote.NoteStore.NoteFilter({
tagGuids,
ascending: false,
order: Evernote.Types.NoteSortOrder.UPDATED
});
const spec = this.getDefaultResultSpec();
return this.noteStore.findNotesMetadata(filter, 0, maxResults, spec);
}
getDefaultResultSpec() {
return new Evernote.NoteStore.NotesMetadataResultSpec({
includeTitle: true,
includeCreated: true,
includeUpdated: true,
includeTagGuids: true,
includeNotebookGuid: true
});
}
}
module.exports = SearchService;
Step 2: Advanced Search Grammar Builder
class QueryBuilder {
constructor() {
this.parts = [];
}
notebook(name) {
this.parts.push(`notebook:"${name}"`);
return this;
}
tag(name) {
this.parts.push(`tag:"${name}"`);
return this;
}
excludeTag(name) {
this.parts.push(`-tag:"${name}"`);
return this;
}
intitle(text) {
this.parts.push(`intitle:"${text}"`);
return this;
}
createdAfter(date) {
this..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..();
;
}
() {
..(query);
;
}
() {
..();
;
}
() {
..();
;
}
() {
. = ;
;
}
() {
. = ;
;
}
() {
.();
}
() {
.();
}
() {
.();
}
() {
..();
;
}
() {
( input === ) {
input;
}
input.().(, ).(, );
}
() {
query = ..();
(.) {
query = + query;
}
query;
}
() {
. = [];
. = ;
. = ;
;
}
}
. = ;
Step 3: Paginated Search Results
class SearchService {
async *paginatedSearch(query, pageSize = 50) {
const filter = new Evernote.NoteStore.NoteFilter({
words: query,
ascending: false,
order: Evernote.Types.NoteSortOrder.UPDATED
});
const spec = this.getDefaultResultSpec();
let offset = 0;
let hasMore = true;
while (hasMore) {
const result = await this.noteStore.findNotesMetadata(
filter,
offset,
pageSize,
spec
);
for (const note of result.notes) {
yield note;
}
offset += result.notes.length;
hasMore = offset < result.totalNotes;
}
}
async getAllMatching(query, maxTotal = 1000) {
allNotes = [];
pageSize = ;
( note .(query, pageSize)) {
allNotes.(note);
(allNotes. >= maxTotal) ;
}
allNotes;
}
() {
metadata = .(query, { maxResults });
notesWithContent = .(
metadata..( note => {
fullNote = ..(
note.,
,
,
,
);
fullNote;
})
);
notesWithContent;
}
}
Step 4: Related Notes Discovery
class SearchService {
async findRelatedNotes(noteGuid) {
const relatedResult = await this.noteStore.findRelated(
new Evernote.NoteStore.RelatedQuery({
noteGuid
}),
new Evernote.NoteStore.RelatedResultSpec({
maxNotes: 10,
maxNotebooks: 3,
maxTags: 10
})
);
return {
notes: relatedResult.notes || [],
notebooks: relatedResult.notebooks || [],
tags: relatedResult.tags || []
};
}
async findRelatedToText(text) {
const relatedResult = await this.noteStore.findRelated(
new Evernote.NoteStore.RelatedQuery({
plainText: text
}),
new ..({
:
})
);
relatedResult. || [];
}
() {
filter = ..({
notebookGuid,
: ,
: ...
});
spec = .();
..(filter, , maxResults, spec);
}
() {
filter = ..({
: ,
: ...
});
spec = .();
..(filter, , limit, spec);
}
}
Step 5: Search Result Enrichment
class SearchService {
async enrichResults(searchResult) {
if (!this._notebookCache) {
const notebooks = await this.noteStore.listNotebooks();
this._notebookCache = new Map(
notebooks.map(nb => [nb.guid, nb])
);
}
if (!this._tagCache) {
const tags = await this.noteStore.listTags();
this._tagCache = new Map(
tags.map(t => [t.guid, t])
);
}
return searchResult.notes.map(note => ({
guid: note.guid,
title: note.title,
created: (note.),
: (note.),
: ..(note.)?.,
: (note. || []).(
..(guid)?.
).(),
: note.
}));
}
() {
result = .(query, options);
enrichedNotes = .(result);
{
: enrichedNotes,
: result.,
: result.
};
}
}
Step 6: Complete Search Workflow Example
const Evernote = require('evernote');
const SearchService = require('./services/search-service');
const QueryBuilder = require('./utils/query-builder');
async function searchWorkflow(accessToken) {
const client = new Evernote.Client({
token: accessToken,
sandbox: true
});
const noteStore = client.getNoteStore();
const search = new SearchService(noteStore);
const qb = new QueryBuilder();
const todoQuery = qb
.thisWeek()
.hasUncompletedTodos()
.build();
console.log('Query:', todoQuery);
const todos = await search.searchEnriched(todoQuery);
console.log(`Found ${todos.totalNotes} notes with uncompleted todos`);
qb.();
meetingQuery = qb
.()
.()
.()
.();
meetings = search.(meetingQuery);
.();
qb.();
attachmentQuery = qb
.()
.()
.();
withAttachments = search.(attachmentQuery);
.();
qb.();
urgentQuery = qb
.()
.()
.()
.();
urgent = search.(urgentQuery);
.();
urgent..( {
.();
});
(urgent.. > ) {
related = search.(urgent.[].);
.();
related..( {
.();
});
}
{
todos,
meetings,
withAttachments,
urgent
};
}
. = searchWorkflow;
Output
- Flexible search service with query builder
- Search grammar support for complex queries
- Paginated results for large datasets
- Related notes discovery
- Enriched results with notebook/tag names
Search Grammar Quick Reference
| Operator | Example | Description |
|---|
notebook: | notebook:"Work" | Restrict to notebook |
tag: | tag:urgent | Has tag |
-tag: | -tag:archived | Exclude tag |
intitle: | intitle:meeting | Word in title |
created: | created:day-7 | Created after |
updated: | updated:week | Updated after |
todo: | todo:false | Has uncompleted todos |
resource: | resource:image/* | Has attachment type |
any: | any: term1 term2 | Match ANY term |
"phrase" | "exact match" | Exact phrase |
prefix* | meet* | Wildcard suffix |
Error Handling
| Error | Cause | Solution |
|---|
RATE_LIMIT_REACHED | Too many searches | Add delay between requests |
INVALID_SEARCH | Bad search grammar | Validate query syntax |
QUOTA_REACHED | Search quota exceeded | Reduce search frequency |
Resources
Next Steps
For error handling patterns, see evernote-common-errors.