fuzzy-searching, parallax
This commit is contained in:
		
							parent
							
								
									5a1467c971
								
							
						
					
					
						commit
						817057a861
					
				
							
								
								
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -2736,6 +2736,11 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "mongoose-fuzzy-searching": { | ||||||
|  |       "version": "1.3.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/mongoose-fuzzy-searching/-/mongoose-fuzzy-searching-1.3.1.tgz", | ||||||
|  |       "integrity": "sha512-Vi+EwmYPoxZzgwBOuTg5FBjXqbX1gjbXxvX/4Ypo7yC2aGKwKfgEeenAVwU6DPirL/TDj6jLGEoblzMJ3+HWCg==" | ||||||
|  |     }, | ||||||
|     "mongoose-legacy-pluralize": { |     "mongoose-legacy-pluralize": { | ||||||
|       "version": "1.0.2", |       "version": "1.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", |       "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ | |||||||
|     "express": "^4.17.1", |     "express": "^4.17.1", | ||||||
|     "express-session": "^1.17.1", |     "express-session": "^1.17.1", | ||||||
|     "mongoose": "^5.9.18", |     "mongoose": "^5.9.18", | ||||||
|  |     "mongoose-fuzzy-searching": "^1.3.1", | ||||||
|     "multer": "^1.4.2", |     "multer": "^1.4.2", | ||||||
|     "passport": "^0.4.1", |     "passport": "^0.4.1", | ||||||
|     "passport-local": "^1.0.0", |     "passport-local": "^1.0.0", | ||||||
|  | |||||||
| @ -1,6 +1,15 @@ | |||||||
| <script> | <script> | ||||||
|     import { stores } from '@sapper/app'; |     import { stores, goto } from '@sapper/app'; | ||||||
|     const { session } = stores(); |     const { session } = stores(); | ||||||
|  | 
 | ||||||
|  |     let query = ''; | ||||||
|  | 
 | ||||||
|  |     function search(e) | ||||||
|  |     { | ||||||
|  |         if (e.keyCode === 13) { | ||||||
|  |             goto(`/search/${encodeURIComponent(query)}`); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
| @ -23,11 +32,10 @@ | |||||||
|         padding: 0 1.5rem 0.25rem; |         padding: 0 1.5rem 0.25rem; | ||||||
|         align-items: start; |         align-items: start; | ||||||
|     } |     } | ||||||
|     div.filler { |     div.search { | ||||||
|         flex: 1 0 0; |         flex: 1 0 0; | ||||||
|     } |         margin: auto 0; | ||||||
|     div.items div { |         display: flex; | ||||||
|         display: block; |  | ||||||
|     } |     } | ||||||
|     div.link a { |     div.link a { | ||||||
|         text-decoration: none; |         text-decoration: none; | ||||||
| @ -48,12 +56,21 @@ | |||||||
|             margin-left: 4rem; |             margin-left: 4rem; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     input.search { | ||||||
|  |         height: 1.5rem; | ||||||
|  |         width: 50%; | ||||||
|  |         font-size: 1rem; | ||||||
|  |         margin: 0 auto; | ||||||
|  |         text-align: center; | ||||||
|  |     } | ||||||
| </style> | </style> | ||||||
| 
 | 
 | ||||||
| <nav> | <nav> | ||||||
|     <div class="items"> |     <div class="items"> | ||||||
|         <div><a href="/"><img class="wordmark" src="/logo.png" alt="HowFeed.biz"></a></div> |         <div><a href="/"><img class="wordmark" src="/logo.png" alt="HowFeed.biz"></a></div> | ||||||
|         <div class="filler"></div> |         <div class="search"> | ||||||
|  |             <input class="search" on:keyup={search} bind:value={query} type="text" placeholder="Search"> | ||||||
|  |         </div> | ||||||
|         {#if !$session.user} |         {#if !$session.user} | ||||||
|             <div class="link"><a href="/contact">Contact Us</a></div> |             <div class="link"><a href="/contact">Contact Us</a></div> | ||||||
|         {:else} |         {:else} | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import mongoose from 'mongoose'; | import mongoose from 'mongoose'; | ||||||
|  | import mongoose_fuzzy_searching from 'mongoose-fuzzy-searching'; | ||||||
| 
 | 
 | ||||||
| const { Schema } = mongoose; | const { Schema } = mongoose; | ||||||
| const ArticleSchema = new Schema({ | const ArticleSchema = new Schema({ | ||||||
| @ -28,13 +29,18 @@ const ArticleSchema = new Schema({ | |||||||
| 
 | 
 | ||||||
| ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-'); | ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-'); | ||||||
| 
 | 
 | ||||||
| ArticleSchema.pre('save', function (next) { | ArticleSchema.plugin(mongoose_fuzzy_searching, { | ||||||
|     var article = this; |     fields: ['html', 'title'], | ||||||
|     // only gen the slug if title has been modified (or is new)
 |     middlewares: { | ||||||
|     if (!article.isModified('title')) return next(); |         preSave: function () { | ||||||
|  |             var article = this; | ||||||
|  |             // only gen the slug if title has been modified (or is new)
 | ||||||
|  |             if (!article.isModified('title')) | ||||||
|  |                 return; | ||||||
| 
 | 
 | ||||||
|     article.slug = article.genSlug(article.title); |             article.slug = article.genSlug(article.title); | ||||||
|     next(); |         } | ||||||
|  |     } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default mongoose.model('Article', ArticleSchema); | export default mongoose.model('Article', ArticleSchema); | ||||||
|  | |||||||
| @ -83,7 +83,7 @@ | |||||||
|     async function addCategory() |     async function addCategory() | ||||||
|     { |     { | ||||||
|         let name = prompt('Enter a category name'); |         let name = prompt('Enter a category name'); | ||||||
|         if (name) { |         if (name !== null) { | ||||||
|             const res = await fetch('/cms/category', { |             const res = await fetch('/cms/category', { | ||||||
|                 method: 'POST', |                 method: 'POST', | ||||||
|                 headers: { |                 headers: { | ||||||
| @ -92,8 +92,13 @@ | |||||||
|                 }, |                 }, | ||||||
|                 body: JSON.stringify({ name }) |                 body: JSON.stringify({ name }) | ||||||
|             }); |             }); | ||||||
|             categories = await res.json(); |             const json = await res.json(); | ||||||
|             category = categories.filter(c => c.name === name)[0].slug; |             if (res.status === 200) { | ||||||
|  |                 categories = json; | ||||||
|  |                 category = categories.filter(c => c.name === name)[0].slug; | ||||||
|  |             } else { | ||||||
|  |                 alert(`Error ${res.status}: ${json.message}`); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
|  | |||||||
| @ -36,7 +36,7 @@ | |||||||
|             <h3>James N. Shiffer</h3> |             <h3>James N. Shiffer</h3> | ||||||
|             <p>Webmaster</p> |             <p>Webmaster</p> | ||||||
|             <p>FemboyFinancial Holdings Co., Ltd. (USA LLC)</p> |             <p>FemboyFinancial Holdings Co., Ltd. (USA LLC)</p> | ||||||
|             <p><a href="mailto:webmaster@femboyfashion.com.hk">webmaster@femboyfashion.com.hk</a></p> |             <p><a href="mailto:webmaster@howfeed.biz">webmaster@howfeed.biz</a></p> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|     <div style="max-width:100%;margin:0 auto;width:700px;height:440px;"> |     <div style="max-width:100%;margin:0 auto;width:700px;height:440px;"> | ||||||
|  | |||||||
							
								
								
									
										55
									
								
								src/routes/search/[query].svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/routes/search/[query].svelte
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | <script context="module"> | ||||||
|  |     export async function preload({ params }) | ||||||
|  |     { | ||||||
|  |         let query = params.query; | ||||||
|  |         const res = await this.fetch(`/search.json?query=${encodeURIComponent(query)}`); | ||||||
|  |         const json = await res.json(); | ||||||
|  |         if (res.status === 200) { | ||||||
|  |             return { query, results: json }; | ||||||
|  |         } else { | ||||||
|  |             this.error(res.status, res.message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <script> | ||||||
|  |     export let query; | ||||||
|  |     export let results; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |     h1 { | ||||||
|  |         margin: 0 auto; | ||||||
|  |         color: whitesmoke; | ||||||
|  |         margin-top: 1rem; | ||||||
|  |         font-size: 2rem; | ||||||
|  |         font-size: 3rem; | ||||||
|  |         text-transform: uppercase; | ||||||
|  |         text-align: center; | ||||||
|  |         max-width: 80%; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | <svelte:head> | ||||||
|  |     <title>Search Results for: {query} | HOWFEED.BIZ</title> | ||||||
|  | </svelte:head> | ||||||
|  | 
 | ||||||
|  | <div class="background"></div> | ||||||
|  | <div class="floaty"> | ||||||
|  |     <h1>Search Results for: {query}</h1> | ||||||
|  |     <div class="article-list"> | ||||||
|  |     {#each results as {title, slug, image, created_at}} | ||||||
|  |         <a rel="prefetch" href={`/a/${slug}`}> | ||||||
|  |             <figure class="article-image"> | ||||||
|  |                 <img src={image || '/logo.png'} alt={title}> | ||||||
|  |             </figure> | ||||||
|  |             <div class="article-meta"> | ||||||
|  |                 <p class="article-title">{title}</p> | ||||||
|  |                 <p class="article-date">{new Date(created_at).toLocaleDateString()}</p> | ||||||
|  |             </div> | ||||||
|  |         </a> | ||||||
|  |     {:else} | ||||||
|  |         <p>No results found :(</p> | ||||||
|  |     {/each} | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										20
									
								
								src/routes/search/index.json.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/routes/search/index.json.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | import Article from '../../models/article.js'; | ||||||
|  | 
 | ||||||
|  | export async function get(req, res) | ||||||
|  | { | ||||||
|  |     const { query } = req.query; | ||||||
|  |     if (!query) { | ||||||
|  |         res.writeHead(422, { | ||||||
|  |             'Content-Type': 'application/json' | ||||||
|  |         }); | ||||||
|  |         res.end(JSON.stringify({ | ||||||
|  |             message: `No search query provided` | ||||||
|  |         })); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     const results = await Article.fuzzySearch(query); | ||||||
|  |     res.writeHead(200, { | ||||||
|  |         'Content-Type': 'application/json' | ||||||
|  |     }); | ||||||
|  |     res.end(JSON.stringify(results)); | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								src/routes/search/index.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/routes/search/index.svelte
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | <script> | ||||||
|  |     import { goto } from '@sapper/app'; | ||||||
|  | 
 | ||||||
|  |     let query = ''; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |     input { | ||||||
|  |         width: 440px; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | 
 | ||||||
|  | <div class="content"> | ||||||
|  |     <h1>Search Results</h1> | ||||||
|  |     <p>You haven't entered any search terms.</p> | ||||||
|  |     <input type="text" placeholder="Search Articles" bind:value={query}> | ||||||
|  |     <button type="submit" on:click={() => { goto(`/search/${encodeURIComponent(query)}`); }}>Search</button> | ||||||
|  | </div> | ||||||
| @ -115,6 +115,7 @@ div.article-meta { | |||||||
| div.background { | div.background { | ||||||
|     background: url('/cityscape.jpg') no-repeat center; |     background: url('/cityscape.jpg') no-repeat center; | ||||||
|     background-size: cover; |     background-size: cover; | ||||||
|  |     background-attachment: fixed; | ||||||
|     position: fixed; |     position: fixed; | ||||||
|     height: 24rem; |     height: 24rem; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user