This extension provides an advanced search interface for oCIS with comprehensive filter support, including custom photo EXIF metadata fields added to the backend.
┌─────────────────────────────────────────────────────────────────┐
│ oCIS Web UI │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────┐ ┌──────────────────────────────┐ │
│ │ Advanced Search │ │ Photo Gallery │ │
│ │ (This Addon) │ │ (ocis-photo-addon) │ │
│ └──────────┬───────────┘ └──────────────┬───────────────┘ │
│ │ │ │
│ │ @ownclouders/web-pkg │ │
│ │ ┌─────────────────────┐ │ │
│ └──┤ useSearch() ├─────┘ │
│ │ buildSearchTerm() │ │
│ └──────────┬──────────┘ │
│ │ │
└───────────────────────────┼──────────────────────────────────────┘
│
│ KQL Query
▼
┌─────────────────────────────────────────────────────────────────┐
│ oCIS Backend │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────┐ ┌───────────────────────────────┐ │
│ │ WebDAV Search │ │ Search Service │ │
│ │ Endpoint │◄─────┤ (KQL Parser + Bleve Query) │ │
│ └───────────────────┘ └───────────────┬───────────────┘ │
│ │ │
│ ┌───────────────▼───────────────┐ │
│ │ Bleve Index │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ Standard Fields: │ │ │
│ │ │ - Name, Path, Size │ │ │
│ │ │ - MimeType, Mtime │ │ │
│ │ │ - Tags, Content │ │ │
│ │ ├──────────────────────────┤ │ │
│ │ │ Photo Fields (custom): │ │ │
│ │ │ - photo.cameraMake │ │ │
│ │ │ - photo.cameraModel │ │ │
│ │ │ - photo.takenDateTime │ │ │
│ │ │ - photo.iso │ │ │
│ │ │ - photo.fNumber │ │ │
│ │ │ - photo.focalLength │ │ │
│ │ └──────────────────────────┘ │ │
│ └───────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
src/
├── index.ts # Extension entry point
│ └── defineWebApplication() # Registers app in oCIS
│
├── views/
│ └── AdvancedSearchView.vue # Main view (route: /advanced-search)
│ ├── Search input
│ ├── Filter toggle
│ ├── Active filter chips
│ ├── Filter panel
│ ├── Results display
│ └── Saved queries panel
│
├── components/
│ ├── SearchFilters.vue # Filter panel component
│ │ ├── Standard filters section
│ │ └── Photo/EXIF filters section
│ │
│ ├── FilterChip.vue # Active filter display chip
│ │ └── Remove button
│ │
│ └── SearchResults.vue # Results display
│ ├── List view
│ ├── Grid view
│ └── Table view
│
├── composables/
│ ├── useAdvancedSearch.ts # Core search logic
│ │ ├── buildKQLQuery() # Converts filters to KQL
│ │ ├── executeSearch() # Calls search API
│ │ ├── activeFilters # Computed filter chips
│ │ └── state management
│ │
│ └── useSearchHistory.ts # Saved queries
│ ├── saveQuery()
│ ├── deleteQuery()
│ └── localStorage persistence
│
└── types/
└── index.ts # TypeScript definitions
├── SearchFilters # Filter state types
├── ActiveFilter # Chip display type
├── SearchResults # Results container
└── SavedQuery # Persisted query type
User Input Filter State KQL String
─────────── ────────────── ─────────────
Name: "vacation*" → standard.name: "vacation*" → name:vacation*
Type: "file" → standard.type: "file" → type:file
Media: "image/*" → standard.mediaType: "..." → mediatype:image/*
Camera: "Canon" → photo.cameraMake: "Canon" → photo.cameraMake:Canon
ISO: 100-800 → photo.isoRange: {min,max} → photo.iso:[100 TO 800]
Combined: name:vacation* AND type:file AND mediatype:image/* AND photo.cameraMake:Canon AND photo.iso:[100 TO 800]
1. User clicks "Search" or presses Enter
2. useAdvancedSearch.executeSearch() is called
3. kqlQuery computed property builds the query string
4. web-pkg's useSearch().search(kql, limit) is called
5. Results are parsed and stored in state.results
6. SearchResults component renders the items
activeFilters (computed)
├── Reads from state.filters
├── Creates ActiveFilter objects for each non-empty filter
└── Returns array for FilterChip rendering
removeFilter(filterId)
├── Identifies filter by ID
├── Clears the specific filter in state.filters
└── Reactive update triggers re-render
| UI Control | Filter Property | KQL Output |
|---|---|---|
| Name input | standard.name | name:*{value}* |
| Type select | standard.type | type:{value} |
| Media type | standard.mediaType | mediatype:{value} |
| Size range | standard.sizeRange | size:[{min} TO {max}] |
| Modified range | standard.modifiedRange | mtime:[{start} TO {end}] |
| Tags input | standard.tags | tags:{value} or (tags:a OR tags:b) |
| Content input | standard.content | content:{value} |
| UI Control | Filter Property | KQL Output |
|---|---|---|
| Camera make | photo.cameraMake | photo.cameraMake:{value} |
| Camera model | photo.cameraModel | photo.cameraModel:{value} |
| Date taken range | photo.takenDateRange | photo.takenDateTime:[{start} TO {end}] |
| ISO range | photo.isoRange | photo.iso:[{min} TO {max}] |
| Aperture range | photo.fNumberRange | photo.fNumber:[{min} TO {max}] |
| Focal length range | photo.focalLengthRange | photo.focalLength:[{min} TO {max}] |
Special characters in values are escaped with backslash:
+ - = & | > < ! ( ) { } [ ] ^ " ~ : \ / space- Exception: Wildcards
*and?are preserved if they appear intentional
state = reactive<AdvancedSearchState>({
filters: SearchFilters, // Current filter configuration
results: SearchResults, // Search results with pagination
loading: boolean, // Loading indicator
error: string | null, // Error message
kqlQuery: string, // Built query (debug display)
viewMode: ResultViewMode, // list | grid | table
sort: SortConfig, // Sorting configuration
})// localStorage key: 'ocis-advanced-search-saved-queries'
SavedQuery[] = [
{
id: string,
name: string,
filters: SearchFilters,
savedAt: ISO date string
}
]This extension requires the custom oCIS build with photo metadata search support:
- Bleve Index must have
Store=truefor photo fields - KQL Parser must recognize
photo.*field prefixes - WebDAV Search must return
oc:photo-*properties
See: /home/AIScripts/ocis-upstream/ (branch: feature/photo-metadata-search)
1. Vite compiles Vue SFC to JavaScript
2. Output is AMD format (anonymous module)
3. CSS is bundled separately
4. manifest.json copied from public/
/data/owncloud/ocis/web/assets/apps/advanced-search/
├── index.js # AMD module (from dist/index.amd.js)
├── style.css # Component styles
└── manifest.json # App manifest
oCIS auto-discovers extensions in the assets path and registers them in config.json:
{
"external_apps": [
{ "id": "advanced-search", "path": "/assets/apps/advanced-search/index.js" }
]
}