Skip to content

cellomatt/aurora

Repository files navigation


Aurora is a clone of Quora with an added "ranking" of questions by expertise level. The expertise level ranking allows the questions to be easily accessed by novice, intermediate and expert levels of difficulty. Logo

  • Users can log in or sign up to access some functionality the site.
  • A logged in user has the ability to post questions with both a topic and expertise level.
  • Posted questions can be answered, and answers can be commented on.
  • The search bar can find questions using a search term and SQL queries.
  • Search results can be sorted by topic or expertise level.

Try the site live: Here | Check out our documentation

How to run the site locally

  • Clone the repo
  • Use the command npm install to install all dependencies
  • Make a copy of the .env.example file and edit to match local db configuration
  • Create the database and user in psql
    • Run all migrations with npx dotenv sequelize db:migrate
    • Seed all data with npx dotenv sequelize db:seed:all
  • Use the start script npm start to run the server

Technologies used in Aurora

Sequelize was used to store and easily manage data with its amazing data models and migrations.

Express JS was our framework and it reduced a ton of boiler plate code, freeing us to implement more features.

npm was our software registry, and within it we installed many packages; some notable examples include:

  • nodemon,
  • morgan,
  • express-validator,
  • and more

Pug is an excellent view engine we used to quickly and beautifully display HTML on our pages.

Heroku is the web hosting app of our choice that allowed us to run our app on the cloud!

Honorable Mentions are the developement tools that made life much more enjoyable!

  • Postman made route testing very easy and fun!
  • Postbird, its wonderful GUI made all the difference!

Features that we implemented

The first big feature we tackled is the searching algorithm, which populates the page with results containing either a question's title or its message. ``` const { searchTerm } = req.body;

const results = await Question.findAll({

      where: {
           [Op.or]: [{
                   title: {
                       [Op.iLike]: '%' + searchTerm + '%'
                   }
               },
               {
                   message: {
                       [Op.iLike]: '%' + searchTerm + '%'
                   }
               }
           ]
       },
       include:
            [Topic, Expertise, User]
       ,
       order: [
           ['createdAt', 'DESC']
       ]

   })
```
How it was done
  1. We started by extracting the search term from the POST request.

    const {
        searchTerm
    } = req.body;
    
  2. Then we queried the database for questions where either the question title or the question message (case insensitive) matched the search term.

    const results = await Question.findAll({
            where: {
                [Op.or]: [{
                        title: {
                            [Op.iLike]: '%' + searchTerm + '%'
                        }
                    },
                    {
                        message: {
                            [Op.iLike]: '%' + searchTerm + '%'
                        }
                    }
                ]
            },
    
        })
    
  3. We included each question's topic, expertise level, and user, and ordered the results so that the most recent question appears first.

    include:
             [Topic, Expertise, User]
        ,
        order: [
            ['createdAt', 'DESC']
        ]
    

The other big feature that we implemented was a sorting algorithm on our search results.

```
document.addEventListener('DOMContentLoaded', ev => {
    localStorage.clear();
})

const filterText = id => {

    let select = document.getElementById(id);
    let option = select.value;

    if (id === 'expertiseSelect') {
        localStorage.setItem('expertiseSort', option);
    } else {
        localStorage.setItem('topicSort', option);
    }

    let localTopic = localStorage.getItem('topicSort');
    let localExpertise = localStorage.getItem('expertiseSort');

    if (!localTopic) localStorage.setItem('topicSort', 'All');
    if (!localExpertise) localStorage.setItem('expertiseSort', 'All');

    localTopic = localStorage.getItem('topicSort');
    localExpertise = localStorage.getItem('expertiseSort');

    let divs = document.querySelectorAll(".result");

    divs.forEach((div) => {
        displayCombinator(localTopic, localExpertise, div);
    });
};

function displayCombinator(topic, expertise, div) {
    if (topic === 'All' && expertise === 'All') {
        div.style.display = 'flex';
    } else {
        if (topic === 'All' && expertise !== 'All') {
            if (div.classList.contains(expertise)) {
                div.style.display = "flex";
            } else {
                div.style.display = "none";
            }
        } else if (expertise === 'All' && topic !== 'All') {
            if (div.classList.contains(topic)) {
                div.style.display = "flex";
            } else {
                div.style.display = "none";
            }
        } else if (div.classList.contains(topic) && div.classList.contains(expertise)) {
            div.style.display = 'flex';
        } else {
            div.style.display = 'none';
        }
    }
}
```
How it was done
  1. We started by populating the dropdown menus for Topic and Expertise Level on the search results page to reflect the topics and expertise levels of the result questions:

    let topicIds = []
    let expertiseIds = [];
    
    results.forEach((result) => {
        if (!topicIds.includes(result.Topic.id)) {
            topicIds.push(result.Topic.id)
        }
        if (!expertiseIds.includes(result.Expertise.id)) {
            expertiseIds.push(result.Expertise.id)
        }
    })
    
    const topics = await Topic.findAll({ where: {
        id: {
            [Op.in]: topicIds
        }
    }})
    
    const expertises = await Expertise.findAll({ where: {
        id: {
            [Op.in]: expertiseIds
        }
    }})
    
  2. Then we cleared local storage when the search results page was loaded in order to make space for our sorting function variables:

    document.addEventListener('DOMContentLoaded', ev => {
    localStorage.clear();
    })
    
  3. We rendered the dropdown select menus with the content from our query in step 1, then set up an event listener to save the selected value to local storage:

    div.sort_bar
      select#topicSelect(name="topicId" class="form__dropdown" onchange="filterText('topicSelect')")
        option(value="" disabled selected hidden) Topic
        option(value="All") All
        each topic in topics
          option(value=topic.id class="form__dropdown--option")=topic.name
    
      select#expertiseSelect(name="expertiseId" class="form__dropdown" onchange="filterText('expertiseSelect')")
        option(value="" disabled selected hidden) Expertise Level
        option(value="All") All
        each expertise in expertises
          option(value=expertise.description class="form__dropdown--option")=expertise.description
    
    const filterText = id => {
    
      let select = document.getElementById(id);
      let option = select.value;
    
      if (id === 'expertiseSelect') {
          localStorage.setItem('expertiseSort', option);
      } else {
          localStorage.setItem('topicSort', option);
      }
    
      let localTopic = localStorage.getItem('topicSort');
      let localExpertise = localStorage.getItem('expertiseSort');
    
      if (!localTopic) localStorage.setItem('topicSort', 'All');
      if (!localExpertise) localStorage.setItem('expertiseSort', 'All');
    
      localTopic = localStorage.getItem('topicSort');
      localExpertise = localStorage.getItem('expertiseSort');
    };
    
  4. We called a helper function on each of our result divs to filter results based on the variables in local storage and render them dynamically:

    let divs = document.querySelectorAll(".result");
    
      divs.forEach((div) => {
          displayCombinator(localTopic, localExpertise, div);
    
    function displayCombinator(topic, expertise, div) {
      if (topic === 'All' && expertise === 'All') {
          div.style.display = 'flex';
      } else {
          if (topic === 'All' && expertise !== 'All') {
              if (div.classList.contains(expertise)) {
                  div.style.display = "flex";
              } else {
                  div.style.display = "none";
              }
          } else if (expertise === 'All' && topic !== 'All') {
              if (div.classList.contains(topic)) {
                  div.style.display = "flex";
              } else {
                  div.style.display = "none";
              }
          } else if (div.classList.contains(topic) && div.classList.contains(expertise)) {
              div.style.display = 'flex';
          } else {
              div.style.display = 'none';
          }
      }
    }
    

Challenges throughout the development process

We faced a few challenges while we were building Aurora:

  1. We encountered a merge issue with one of our features that took a long time to sort out. Make sure you stay up to date with main, folks!

  2. It took us a long time to figure out what the best way to sort our search results was. Thankfully, we were able to reference some other people's strategies and come up with something that fit our project.

Developers

Developer

Antonio A. Matt K. Quintin H. Max L.
@vantanova @cellomatt @QuintinHull @lettemax

Thank you for reading our project README ❤️

About

A clone of Quora with an added "ranking" of questions by expertise level.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •