How to increment property in MongoDB collection using $inc

Viewed 687

I have a list of articles that have a property views and I want to increment that property in the database each time a user clicks on an article title. Currently nothing happens when I do it. Why isn't it working and how can I increment that property each time on click? Here is my React part:

  const incrementViews = (id) => {
        var item = posts.find(x => x._id === id);
        item.views += 1;
    }

      <div className="post-title">
          <Link to={`/post/${post._id}`}>
    <h2><a href="#" onClick={() => incrementViews(post._id)}>{post.title}</a>
       </h2>
        </Link>
 </div>

and my server.js:

// Requiring the dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3001;
const BASE_URL = process.env.REACT_APP_BASE_URL;
console.log(BASE_URL)
const itemRoutes = express.Router();
let Comment = require('./comment.model');

app.use(cors());
app.use(bodyParser.json());

mongoose.connect(BASE_URL, { useNewUrlParser: true })

const connection = mongoose.connection;

connection.once('open', function () {
  console.log('Connection to MongoDB established succesfully!');
});

let collection = connection.collection("posts_with_tags_test");
collection.createIndex(
  {
    postContent: 'text',
    title: 'text'
  }
);


// Serve static assets
if (process.env.NODE_ENV === 'production') {
  app.use(express.static('build'));
}

itemRoutes.route('/').get(async (req, res) => {
  let collection = connection.collection("posts_with_tags_test");
  let response = await collection.find({})
    .toArray();
  res.send(response);
});


itemRoutes.route('/search').post(async (req, res) => {
  let result = await connection.collection("posts_with_tags_test").find({
    $text: {
      $search: req.body.searchString
    }
  }).toArray();
  res.send(result);
});


itemRoutes.route("increment/:id"").post(async (req, res) => {
  const { id } = req.params;
  collection.updateOne({ _id: id }, { $inc: { views: 1 } });
  return res.status(200).json({ msg: "OK" });
});

itemRoutes.route('/comments').get(async (req, res) => {
  let collection = connection.collection("comments");
  let response = await collection.find({})
    .toArray();
  res.send(response);
});


itemRoutes.route('/comments')
  .post((req, res) => {
    res.setHeader('Content-Type', 'application/json');
    let comment = new Comment(req.body);
    comment.save()
      .then(comment => {
        res.status(200).json({ comment })
      })
      .catch(err => {
        res.status(400).send('failed')
      })
  });


app.use('/', itemRoutes);
app.use('/comments', itemRoutes);
app.use('/search', itemRoutes);
app.use('/increment', itemRoutes);

app.listen(PORT, function () {
  console.log('Server is running on' + ' ' + PORT);
})
2 Answers

I think there are two problems in frontend and backend respectively.

  • Front-end

    You should use post variable as a state variable so as to re-render then component when changes are made on post.

  • Back-end

    There is no issue with increasing view in your code.

    Here, you need to return success status.

The function incrementViews only increments views on the frontend and never sends any data to the API. One way you can make it work is as follows:

  1. server.js
itemRoutes.route("/increment/:id").post(async (req, res) => {
  const { id } = req.params;
  collection.updateOne({ _id: id }, { $inc: { views: 1 } });
  return res.status(200).json({ msg: "OK" });
});
  1. React
const incrementViews = (id) => {
  // Assuming your API server is running on port 5000.
  fetch(`http://localhost:5000/increment/${id}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
    },
  })
    .then((res) => res.json())
    .then(console.log)
    .catch(console.error);
};

Update

The reason you're getting 404 is a missing colon : in the route parameters.

// Notice the :id, colon is important.
itemRoutes.route("/increment/:id").post(async (req, res) => {
  const { id } = req.params;
  // ...
});

Here is a demo reproduced on Glitch. Removed database logic and just added a response messages. I tested the demo using Postman and it works fine.

On a POST request to https://adaptive-sassy-legal.glitch.me/increment/123, should return a response as shown below.

{
  msg: "itemsRoute increment.",
  id: "123"
}

Update 2

Another thing which I forgot to mention in the previous update was to update the middleware.

// Use only `/increment` instead of `increment/:id`.
app.use("/increment", itemRoutes);

Here is an updated demo.

Related