User input for a MongoDB search

百般思念 提交于 2019-12-08 02:45:01

问题


I am currently trying to accept user input so that a user may be able to search the database.

> db.test2.find().pretty()
{
    "_id" : ObjectId("55de8a17f8389e208a1e7d7e"),
    "name" : "john",
    "favorites" : {
        "vegetable" : "spinach",
        "fruit" : "apple",
    }
}
{
    "_id" : ObjectId("55de8a17f8389e208a1f6gg4"),
    "name" : "becky",
    "favorites" : {
        "vegetable" : "spinach",
        "fruit" : "apple",
    }
}
{
    "_id" : ObjectId("55e3b6cbec2740181355b809"),
    "name" : "liz",
    "favorites" : {
        "vegetable" : "spinach",
        "fruit" : "banana",
    }
}

In this example, the user would be able to search for any combination of a person's favorite vegetable, fruit, or both their favorite vegetable and favorite fruit. If the user entered spinach for favorite vegetable, all three would be returned. However, if the user input favorite vegetable = spinach and favorite fruit = apple, only john and becky would be returned.

In MongoDB, you are able to determine which parameter you want to search. I am trying to write my code in a way that if the user leaves a field blank, it should not be searched for.

I have tried

$query = array("favorites.vegetable" => "$userInput", "favorites.fruit" => "$userInput2");

but if either of those fields are left blank, it will not return any results. I thought about trying to use if statements:

if ($vegetable == NULL)
{
    $query = array("favorites.fruit" => "$fruit");
}

else if($fruit == NULL)
{
    $query = array("favorites.vegetable" => "$vegetable");
}

else
{
    $query = array("favorites.vegetable" => "$vegetable", "favorites.fruit" => "$fruit");
}

but if I would like to make my database searchable by more parameters I would have too many conditional statements. Is there any way to make my Mongo search recognize when a field is left blank?


回答1:


The question really is, "Where is the input coming from?". As if you have some sort of structure to the input, then the coding is quite simple to follow a pattern.

As it is, with two basic variables you can clean the code to simply contruct your query based on what is in the variables:

$query = array();

if ( $fruit != NULL ) {
    $query["favorites.fruit"] = $fruit;
}

if ( $vegetable != NULL ) {
    $query["favorites.vegetable"] = $vegetable;
)

Which means you either end up with a $query here that is either blank to match everything or contains the specific arguments ( either one or two ) depending on whether the content was null or not.

If your input has some structure, then you can be a lot more dynamic:

$input = array("fruit" => "apple", "vegetable" => "spinach");

$query = array();

foreach ( $input as $key => $value ) {
    $query["favorites.$key"] = $value;
}

Which is doing the same thing by appending to the $query but in a much more dynamic way than with individual variables.

Also note that as far as MongoDB is concerned, then your document structure is not great. It probably really should look like this:

{
    "_id" : ObjectId("55de8a17f8389e208a1e7d7e"),
    "name" : "john",
    "favorites" : [
        { "type": "vegetable", "name": "spinach" },
        { "type": "fruit", "name": "apple" }
    ]
}

And while it may initially look like the query comes out a bit more complex, removing the "specific paths" in your query for keys like "vegetable" and "fruit" have a whole lot of benefits that make life a lot easier, and also primarilly the "data" can be "indexed". Key names are not indexable for search and therefore loose efficiency:

$input = array("fruit" => "apple", "vegetable" => "spinach");

$query = array();

foreach ( $input as $key => $value ) {
  $query['$and'][] = array(
    'favorites' => array(
      '$elemMatch' => array(
          'type' => $key, 'name' => $value
      )
    )
  );
}

Which is a nice query using $elemMatch to find documents that contain "all" array elements where "both" the "type" and "name" of the specified items in your input list.

Basically looks like this in JSON:

{
  "$and": [
    { "favorites": {
      "$elemMatch": {
          "type": "fruit", "name": "apple"
      }
    }},
    { "favorites": {
      "$elemMatch": {
          "type": "vegetable", "name": "spinach"
      }
    }}
  ]
}

It all comes down to knowing how to manipulate data structures in your chosen language, and that "data structures" are all MongoDB queries really are, as far as your language is concerned.

As for the structure change then condider that finding people who have "vegetables" in their "favorites" now becomes this:

{ "favorites.type": "vegetable" }

Which is nice since "favorites.type" can be indexed, as opposed to:

{ "favorites.vegetable": { "$exists": true } }

Which while this can "technically" use an index, it does not really do so in such a nice way. So changing the way the schema is represented is desirable and gives more flexibility.



来源:https://stackoverflow.com/questions/32303483/user-input-for-a-mongodb-search

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!