skip to Main Content

If I use pymongo’s find_one to query for a specific value then if it’s not exist I’ll simply get a None but If I query for multiple values using find then I’ll get a cursor if at least one of the values exists:

pymongo.MongoClient("Conn string")
db = db_client[some_db_name]
collection = db[some_collection_name]
id_name_queries = [(123,'George'),(234,'Jon'),(521,'Tfu')] # queries
clusters = collection.find({"$or": [
                                        {"$and": [{"ID": pair[0]}, {"Name": pair[1]}]} for pair in id_name_queries 
                                        ]
                                })

Is it possible to get from find a boolean list (or other iterable) with the same length of id_name_queries indicating for each query if it exists in the collection?

ind = [True,False,True] # (234,'Jon') is not in the collection

2

Answers


  1. You can use $documents to store all your search entries and start a $lookup to the collection. Check the lookup result for [] to return found or not.

    db.aggregate([
      {
        "$documents": [
          {
            "ID": 123,
            "Name": "George"
          },
          {
            "ID": 234,
            "Name": "Jon"
          },
          {
            "ID": 521,
            "Name": "Tfu"
          }
        ]
      },
      {
        "$lookup": {
          "from": "collection",
          "localField": "ID",
          "foreignField": "ID",
          "let": {
            "name": "$Name"
          },
          "pipeline": [
            {
              "$match": {
                "$expr": {
                  "$eq": [
                    "$$name",
                    "$Name"
                  ]
                }
              }
            },
            {
              "$limit": 1
            }
          ],
          "as": "searchResult"
        }
      },
      {
        "$group": {
          "_id": null,
          "result": {
            "$push": {
              $ne: [
                [],
                "$searchResult"
              ]
            }
          }
        }
      }
    ])
    

    Mongo Playground for $documents

    • This won’t work. Playground does not support $documents. This is just to demonstrate the syntax.

    Mongo Playground

    • This can demonstrate the actual result of returning boolean array
    Login or Signup to reply.
  2. Here’s one way you might do it using aggregate.

    The "$match" stage reduces the collection as much as possible, while the "$group" stage aggregates just the pairs that may be of interest. Finally, "$replaceWith" outputs whether each id_name_queries element is present.

    N.B.: Index(s) on "ID" and/or "Name" may increase query performance. Also, if no "ID" nor any "Name" from id_name_queries is found in the collection, this aggregate will not return a boolean for each id_name_queries element. It’s probably best to test this, observe the output, and decide how you want to handle that possibility.

    clusters = collection.aggregate([
      {
        "$match": {
          "ID": {"$in": [pair[0] for pair in id_name_queries]},
          "Name": {"$in": [pair[1] for pair in id_name_queries]}
        }
      },
      {
        "$group": {
          "_id": null,
          "pairs": {"$addToSet": {"pair": ["$ID","$Name"]}}
        }
      },
      {
        "$replaceWith": {
          "ind": {
            "$reduce": {
              "input": [[pair[0], pair[1]] for pair in id_name_queries],
              "initialValue": [],
              "in": {
                "$concatArrays": [
                  "$$value",
                  [{"$in": ["$$this","$pairs.pair"]}]
                ]
              }
            }
          }
        }
      }
    ])
    

    Try an adapted version of this on mongoplayground.net.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search