简体   繁体   中英

complicated mongoose pull list of data from api and insert into mongodb if it doesn't already exist

I am connecting to the Yelp API using the RapidAPI module in Nodejs. I am able to request a token, connect, and request data, retrieve that data, and insert the relevant information for each result it into mongodb. Here's where it gets complicated...

Let's say I make a Yelp API request and search for bars. I get a list of bars and insert them into the database. Let's say one of these in the list is "Joe's Bar & Grill". One of the fields in my mongodb is "type" and it's an array. So now, this particular document will look something like this:

{
  id: 'joes-bar-and-grill',
  name: 'Joe\'s Bar & Grill',
  type: ['bar']
}

But then I run another request on the Yelp API on "restaurants", and in this list "Joe's Bar & Grill" shows up again. Instead of inserting a new duplicate document into mongodb, I'd like the existing document to end up looking like this:

{
  id: 'joes-bar-and-grill',
  name: 'Joe\'s Bar & Grill',
  type: ['bar', 'restaurant']
}

In addition to this, let's say I run another request again for "bars", and "Joe's Bar & Grill" comes up again. I don't want it to automatically insert "bar" into the type array again, if "bar" already exists in its array.

I've tried findOneAndUpdate with upsert: true and a $push of new data into the array, but I cannot get it to work at all. Does anyone have any ideas?

You can use findOneAndUpdate , combined with $addToSet (to make sure that an entry in the array only exists once) and $each (to allow passing arrays to $addToSet ):

Bar.findOneAndUpdate({ id : 'joes-bar-and-grill' }, {
  id        : 'joes-bar-and-grill',
  name      : 'Joe\'s Bar & Grill',
  $addToSet : { type : { $each : [ 'restaurant' ] } }
}, { upsert : true })

EDIT : now that you posted your entire code, the problem becomes more obvious.

For one, I'm not sure if the third and fourth arguments that you're passing to Location.update() make sense. As far as I know, the third should be an option object , and the fourth an async function .

Secondly, it looks like you're just ignoring any update errors.

And lastly, this isn't going to work:

for (var i = 0; i < payload.businesses.length; i++) { Location.update(...) }

Because Location.update() is asynchronous, the i variable will get clobbered (you should browse around on SO to find the explanation for that; for example, see this question ).

You're going to need a library that will provide you with better async support, and preferably one that will also help limiting the number of update queries.

Once such library is async , and using it, your code would become something like this:

const async = require('async');

...

async.eachLimit(payload.businesses, 5, function(business, callback) {
  Location.update({ yelpID : business.id }, {
    name      : business.name,
    latitude  : business.location.latitude,
    longitude : business.location.longitude,
    address1  : business.location.address1,
    address2  : business.location.address2,
    address3  : business.location.address3,
    city      : business.location.city,
    state     : business.location.state,
    zip_code  : business.location.zip_code,
    country   : business.location.country,
    timezone  : 'CST'
    $addToSet : { type : 'bar' }
  }, { upsert : true }, callback);
}, function(err) {
  if (err) {
    console.error(err);
  } else {
    console.log('All documents inserted');
  }
});

You may use $addToSet operator

The $addToSet operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array.

$addToSet only ensures that there are no duplicate items added to the set and does not affect existing duplicate elements. $addToSet does not guarantee a particular ordering of elements in the modified set.

If the field is absent in the document to update, $addToSet creates the array field with the specified value as its element.

If the field is not an array, the operation will fail.

The below solution assumes that on each update, you receive a single type and not an array. If the input document is an array itself, you may use robertklep's solution with $each operator

db.mycoll.update(
   { "id" : "joes-bar-and-grill" },
   {
      $set:{
           name : 'Joe\'s Bar & Grill',
      },
      $addToSet : { type : 'restaurant' }
   },
   true, false);

I have also used $set operator.

The $set operator replaces the value of a field with the specified value.

The $set operator expression has the following form:

{ $set: { field1: value1, ... } }

Here is the mongo shell output to explain it further :

> db.mycoll.find({ "id" : "joes-bar-and-grill" });
  // NO RESULT

> db.mycoll.update(
...    { "id" : "joes-bar-and-grill" },
...    {
...       $set:{
...            name : 'Joe\'s Bar & Grill',
...       },
...       $addToSet : { type : 'restaurant' }
...    },
...    true, false);
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("58e719b4d543c5e30d615d59")
})
 // INSERTED A NEW DOCUMENT AS IT DOES NOT EXIST

> db.mycoll.find({ "id" : "joes-bar-and-grill" }); // FINDING THE OBJECT
{ "_id" : ObjectId("58e719b4d543c5e30d615d59"), "id" : "joes-bar-and-grill", "name" : "Joe's Bar & Grill", "type" : [ "restaurant" ] }


> db.mycoll.update(
...    { "id" : "joes-bar-and-grill" },
...    {
...       $set:{
...            name : 'Joe\'s Bar & Grill',
...       },
...       $addToSet : { type : 'bar' }
...    },
...    true, false);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// UPDATING THE DOCUMENT WITH NEW TYPE : "bar"

> db.mycoll.findOne({ "id" : "joes-bar-and-grill" });
{
    "_id" : ObjectId("58e719b4d543c5e30d615d59"),
    "id" : "joes-bar-and-grill",
    "name" : "Joe's Bar & Grill",
    "type" : [
        "restaurant",
        "bar"
    ]
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM