├── LICENSE.md ├── README.md ├── examples └── geosearch-airports │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── bun.lockb │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── edit-pen-icon.webp │ ├── hand-finger-click-icon.webp │ ├── orama-geosearch.svg │ └── orama.svg │ ├── src │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── DrawControl │ │ │ └── index.jsx │ │ └── Map │ │ │ └── index.jsx │ ├── data │ │ └── records-formatted.json │ ├── index.css │ ├── lib │ │ └── orama.js │ └── main.jsx │ ├── tailwind.config.js │ └── vite.config.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2023 OramaSearch Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Orama Examples 2 | 3 | This repository features a collection of sample projects demonstrating practical applications of Orama. 4 | 5 | ## Demo Lists 6 | 7 | - [Geosearch - Airport map](/examples/geosearch-airports/) [[live demo](https://orama-examples-geosearch-airports.vercel.app/)]. Search all the airports in the world by drawing on a map! 8 | 9 | ## License 10 | [Apache 2.0](/LICENSE.md) -------------------------------------------------------------------------------- /examples/geosearch-airports/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | .env 11 | 12 | node_modules 13 | dist 14 | dist-ssr 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /examples/geosearch-airports/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2023 OramaSearch Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /examples/geosearch-airports/README.md: -------------------------------------------------------------------------------- 1 | # Orama Geosearch 2 | 3 | Simple Vite + React app to demonstrate how to use Orama Geosearch with MapBox. 4 | 5 | Live demo: [https://orama-examples-geosearch-airports.vercel.app](https://orama-examples-geosearch-airports.vercel.app) 6 | 7 | # License 8 | [Apache 2.0](/LICENSE.md) -------------------------------------------------------------------------------- /examples/geosearch-airports/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oramasearch/examples/c381192253c725f8c85a552d6201f05763421e87/examples/geosearch-airports/bun.lockb -------------------------------------------------------------------------------- /examples/geosearch-airports/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Orama Demo - Geosearch 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/geosearch-airports/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geosearch-airports", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "standard --fix .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@orama/orama": "^2.0.0-beta.1", 14 | "@types/mapbox-gl": "^2.7.15", 15 | "classnames": "^2.3.2", 16 | "mapbox-gl": "^2.15.0", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-icons": "^4.11.0", 20 | "react-map-gl": "^7.1.6", 21 | "terra-draw": "^0.0.1-alpha.48" 22 | }, 23 | "devDependencies": { 24 | "@typescript-eslint/eslint-plugin": "^6.0.0", 25 | "@typescript-eslint/parser": "^6.0.0", 26 | "@vitejs/plugin-react-swc": "^3.3.2", 27 | "autoprefixer": "^10.4.16", 28 | "eslint": "^8.45.0", 29 | "eslint-plugin-react-hooks": "^4.6.0", 30 | "eslint-plugin-react-refresh": "^0.4.3", 31 | "postcss": "^8.4.31", 32 | "standard": "^17.1.0", 33 | "tailwindcss": "^3.3.3", 34 | "typescript": "^5.0.2", 35 | "vite": "^4.4.5", 36 | "vite-plugin-top-level-await": "^1.3.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/geosearch-airports/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/geosearch-airports/public/edit-pen-icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oramasearch/examples/c381192253c725f8c85a552d6201f05763421e87/examples/geosearch-airports/public/edit-pen-icon.webp -------------------------------------------------------------------------------- /examples/geosearch-airports/public/hand-finger-click-icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oramasearch/examples/c381192253c725f8c85a552d6201f05763421e87/examples/geosearch-airports/public/hand-finger-click-icon.webp -------------------------------------------------------------------------------- /examples/geosearch-airports/public/orama-geosearch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /examples/geosearch-airports/public/orama.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { OramaMap } from './components/Map' 3 | 4 | function App() { 5 | const [searchResults, setSearchResults] = useState() 6 | 7 | return ( 8 | <> 9 |
10 | 11 |
12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 |

Airports

20 |
21 | { 22 | searchResults 23 | ?
{searchResults?.length ?? 0} results
24 | :
No results
25 | } 26 |
27 |
28 |
    29 | {searchResults?.map((hit, i) => ( 30 |
  • 33 |
    34 | {hit.document.iata} - {hit.document.city} 35 |
    36 |
    37 | {hit.document.country} 38 |
    39 |
  • 40 | ))} 41 |
42 |
43 |
44 |
45 | 46 | ) 47 | } 48 | 49 | export default App 50 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/components/DrawControl/index.jsx: -------------------------------------------------------------------------------- 1 | import { TerraDraw, TerraDrawMapboxGLAdapter, TerraDrawPolygonMode, TerraDrawSelectMode } from 'terra-draw' 2 | import { useMap } from 'react-map-gl'; 3 | import { useState, useEffect } from 'react' 4 | 5 | export function DrawControl(props) { 6 | 7 | const { current: map } = useMap(); 8 | 9 | const [mode, setMode] = useState('static') 10 | const [draw, setDraw] = useState() 11 | 12 | const onChangeCallback = (ids, event) => { 13 | const snapshot = draw.getSnapshot().filter((feature) => feature.geometry.type === 'Polygon') 14 | const polygon = snapshot.find((polygon) => polygon.id === ids[0]) 15 | 16 | if (event === 'create' && polygon) { 17 | props.onCreate(polygon) 18 | } 19 | if (event === 'update' && polygon) { 20 | props.onUpdate(polygon) 21 | } 22 | if (event === 'delete') { 23 | props.onDelete(ids) 24 | } 25 | } 26 | 27 | useEffect(() => { 28 | if (!map || draw) { 29 | return 30 | } 31 | 32 | const drawInstance = new TerraDraw({ 33 | adapter: new TerraDrawMapboxGLAdapter({ 34 | map: map.getMap(), 35 | minPixelDragDistanceSelecting: 1 36 | }), 37 | modes: [new TerraDrawPolygonMode({ 38 | styles: { 39 | fillColor: '#f6cd63', 40 | outlineColor: '#faa38f', 41 | outlineWidth: 3, 42 | fillOpacity: 0.1, 43 | closingPointWidth: 5, 44 | closingPointColor: '#faa38f', 45 | closingPointOutlineWidth: 2, 46 | closingPointOutlineColor: '#f5f5f5', 47 | } 48 | }), new TerraDrawSelectMode({ 49 | dragEventThrottle: 3, 50 | flags: { 51 | polygon: { 52 | feature: { 53 | draggable: true, 54 | rotateable: true, 55 | scaleable: true, 56 | coordinates: { 57 | midpoints: true, 58 | draggable: true, 59 | deletable: true, 60 | }, 61 | }, 62 | } 63 | }, 64 | styles: { 65 | selectedPolygonColor: '#f6cd63', 66 | selectedPolygonOutlineColor: '#faa38f', 67 | outlineWidth: 3, 68 | selectionPolygonFillOpacity: 0.2, 69 | selectionPointWidth: 5, 70 | selectionPointColor: '#faa38f', 71 | selectionPointOutlineWidth: 2, 72 | selectionPointOutlineColor: '#f5f5f5', 73 | midPointColor: '#f6cd63', 74 | midPointOutlineColor: '#f5f5f5', 75 | midPointWidth: 3, 76 | midPointOutlineWidth: 2 77 | } 78 | })] 79 | }) 80 | 81 | drawInstance.start() 82 | drawInstance.setMode('polygon') 83 | setMode('polygon') 84 | setDraw(drawInstance) 85 | 86 | // Ensure clear up on dismount 87 | return () => { 88 | drawInstance.stop() 89 | } 90 | }, [map]) 91 | 92 | 93 | useEffect(() => { 94 | const callback = (ids, event) => { 95 | onChangeCallback(ids, event) 96 | } 97 | 98 | if (draw) { 99 | draw.on('change', callback) 100 | } 101 | 102 | // Ensure callbacks are deleted and created correcltly 103 | return () => { 104 | if (draw && callback) { 105 | draw.off('change', callback) 106 | } 107 | } 108 | 109 | }, [draw, onChangeCallback]) 110 | 111 | return <> 112 |
113 | 143 | 144 | 173 |
174 | 175 | } 176 | 177 | DrawControl.defaultProps = { 178 | onCreate: () => { }, 179 | onUpdate: () => { }, 180 | onDelete: () => { } 181 | } 182 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/components/Map/index.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useMemo } from 'react' 2 | import Map, { Marker } from 'react-map-gl' 3 | import { search } from '@orama/orama' 4 | import { FaLocationPin } from 'react-icons/fa6' 5 | import { orama } from '../../lib/orama' 6 | import { DrawControl } from '../DrawControl' 7 | 8 | const VITE_MAPBOX_TOKEN = import.meta.env.VITE_MAPBOX_TOKEN 9 | 10 | if (!VITE_MAPBOX_TOKEN) { 11 | throw Error('VITE_MAPBOX_TOKEN not set in .env file') 12 | } 13 | 14 | export function OramaMap({ onPolygonChange }) { 15 | const [polygons, setPolygons] = useState({}) 16 | const [uniqueLocations, setUniqueLocations] = useState([]) 17 | 18 | useEffect(() => { 19 | const polygonIds = Object.keys(polygons) 20 | 21 | const oramaPromises = [] 22 | 23 | for (const id of polygonIds) { 24 | const polygon = polygons[id] 25 | const coordinates = [] 26 | 27 | if (!polygon.geometry.coordinates || !polygon.geometry.coordinates[0]) { 28 | return 29 | } 30 | 31 | for (const point of polygon.geometry.coordinates[0]) { 32 | coordinates.push({ lat: point[1], lon: point[0] }) 33 | } 34 | 35 | oramaPromises.push(search(orama, { 36 | limit: 10_000, 37 | where: { 38 | location: { 39 | polygon: { 40 | coordinates 41 | } 42 | } 43 | } 44 | }) 45 | .then((data) => { 46 | if (!data || !data.hits) { 47 | return 48 | } 49 | 50 | return { id: polygon.id, hits: data.hits } 51 | }) 52 | .catch(console.error)) 53 | 54 | } 55 | 56 | // Finally update the search results for all polygons 57 | Promise.all(oramaPromises).then((results) => { 58 | const newSearchResults = {} 59 | results.forEach((result) => { 60 | if (!result) { 61 | return 62 | } 63 | newSearchResults[result.id] = result.hits 64 | }) 65 | 66 | const uniqueLocationIds = new Set() 67 | const uniqueLocations = [] 68 | 69 | // Ensure that locations aren't added twice as this will throw an error 70 | Object.keys(newSearchResults).forEach((polygonId) => { 71 | newSearchResults[polygonId].forEach((location) => { 72 | if (!uniqueLocationIds.has(location)) { 73 | uniqueLocationIds.add(location.id) 74 | uniqueLocations.push(location) 75 | } 76 | }) 77 | }) 78 | 79 | onPolygonChange(uniqueLocations) 80 | setUniqueLocations(uniqueLocations) 81 | }) 82 | 83 | }, [polygons]) 84 | 85 | 86 | const pinMarkers = useMemo(() => { 87 | if (!uniqueLocations || uniqueLocations.length === 0) { 88 | return [] 89 | } 90 | 91 | return uniqueLocations.map((hit) => ( 92 | { 97 | e.originalEvent.stopPropagation() 98 | }} 99 | > 100 | 101 | )) 102 | }, [uniqueLocations]) 103 | 104 | return <> 105 | 118 | { 120 | setPolygons({ 121 | ...polygons, 122 | [polygon.id]: polygon 123 | }) 124 | }} 125 | onUpdate={(polygon) => { 126 | setPolygons({ 127 | ...polygons, 128 | [polygon.id]: polygon 129 | }) 130 | }} 131 | onDelete={(deletedIds) => { 132 | setPolygons((currentPolygons) => { 133 | 134 | const newPolygons = {} 135 | Object.keys(currentPolygons).forEach((polygonId) => { 136 | if (!deletedIds.includes(polygonId)) { 137 | newPolygons[polygonId] = currentPolygons[polygonId] 138 | } 139 | }) 140 | 141 | return newPolygons 142 | }) 143 | }} 144 | /> 145 | {pinMarkers} 146 | 147 | 148 | } 149 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | ul::-webkit-scrollbar { 6 | @apply w-2; 7 | } 8 | 9 | ul::-webkit-scrollbar-track { 10 | @apply bg-transparent; 11 | } 12 | 13 | ul::-webkit-scrollbar-thumb { 14 | @apply bg-zinc-950 rounded-lg; 15 | } 16 | 17 | ul::-webkit-scrollbar-thumb:hover { 18 | @apply bg-gray-950; 19 | } -------------------------------------------------------------------------------- /examples/geosearch-airports/src/lib/orama.js: -------------------------------------------------------------------------------- 1 | import { create, insertMultiple } from '@orama/orama' 2 | import data from '../data/records-formatted.json' 3 | 4 | const db = await create({ 5 | schema: { 6 | id: 'string', 7 | iata: 'string', 8 | country: 'string', 9 | city: 'string', 10 | links: 'number', 11 | location: 'geopoint' 12 | } 13 | }) 14 | 15 | await insertMultiple(db, data) 16 | 17 | export const orama = db 18 | -------------------------------------------------------------------------------- /examples/geosearch-airports/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | import 'mapbox-gl/dist/mapbox-gl.css' 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /examples/geosearch-airports/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | './index.html', 5 | './src/**/*.{js,ts,jsx,tsx}' 6 | ], 7 | theme: { 8 | extend: {} 9 | }, 10 | plugins: [] 11 | } 12 | -------------------------------------------------------------------------------- /examples/geosearch-airports/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | import topLevelAwait from 'vite-plugin-top-level-await' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), topLevelAwait()] 8 | }) 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orama-examples", 3 | "version": "0.0.1", 4 | "description": "Orama Examples", 5 | "workspaces": [ 6 | "packages/*" 7 | ], 8 | "scripts": {}, 9 | "author": { 10 | "name": "Michele Riva", 11 | "email": "michele.riva@oramasearch.com", 12 | "url": "https://github.com/MicheleRiva", 13 | "author": true 14 | }, 15 | "license": "Apache-2.0" 16 | } --------------------------------------------------------------------------------