【问题标题】:How do I filter recipes from the database? [duplicate]如何从数据库中过滤食谱? [复制]
【发布时间】:2021-06-03 14:21:19
【问题描述】:

我有一个食谱收藏,看起来像这样:

{
  "Name": "Omelet",
  "Ingredients": ["Eggs", "Milk", "Butter"]
},
{
  "Name": "Pancakes",
  "Ingredients": ["Eggs", "Milk", "Butter", "Flour", "Sugar", "Salt"]
},
{
  "Name": "Random recipe",
  "Ingredients": ["Eggs", "Milk"]
}

我正在尝试获取包含完全包含在查询中的成分的食谱。例如,如果我有查询鸡蛋、牛奶和黄油,那么我必须从上面的集合中获取煎蛋卷和“随机食谱”,而不是煎饼,因为我没有另外 3 种必要的成分。如果我只有鸡蛋和牛奶,那么它必须只返回“随机食谱”。换句话说,我只想要可以用现有原料制作的食谱。我搜索了文档,但找不到确切的实现方式。有任何想法吗?我在后端使用 Golang,所以如果你在上面写一个例子会更好。如果有任何帮助,我将不胜感激。

现在我写了这个函数,它返回所有带有某些成分的食谱,但这并没有考虑到缺少的成分和不需要所有转移成分的食谱:

func GetTestRecipesFromDB(ingredients []string) *[]Recipe {
    collection := client.Database("food_db").Collection("recipes")

    ctx, _ := context.WithTimeout(context.Background(), 10 * time.Second)

    var recipes []Recipe
    var cursor *mongo.Cursor

    cursor, err := collection.Find(ctx, bson.D{
        {"Ingredients", bson.D{{"$all", ingredients}}},
    })

    if err != nil {
        log.Fatal(err)
        return nil
    }

    if err = cursor.All(ctx, &recipes); err != nil {
        log.Fatal(err)
        return nil
    }

    return &recipes
}

编辑: 根据this answer(感谢@MontgomeryWatts 的建议)我在Go 中写了这个并且它有效:

    query := bson.M{
        "$match" : bson.M{
            "$expr" : bson.M{
                "$setIsSubset": []interface{}{
                    "$Ingredients",
                    ingredients,
                },
            },
        },
    }

    cursor, err := collection.Aggregate(ctx, []bson.M{query})
    if err != nil {
        log.Fatal(err)
        return nil
    }

感谢大家的帮助!

【问题讨论】:

    标签: arrays database mongodb go mongodb-query


    【解决方案1】:

    您可以通过使用$filter 的聚合管道来完成此操作

    • $match 以便您只考虑具有至少一种匹配成分的食谱
    • $addFields 创建OtherIngredient 字段,使用$filter 从Ingredients 中消除查询的成分数组
    • $match 只选择没有其他成分的食谱
    • $project 删除临时字段
    [
      {$match: {Ingredients: {
            $in: ["Eggs","Milk","Butter"]
      }}},
      { $addFields: {
          OtherIngredient: {
            $filter: {
              input: "$Ingredients",
              cond: {$not: {$in: [
                    "$$this",
                    ["Eggs","Milk","Butter"]
              ]}}
            }
          }
      }},
      {$match: {"OtherIngredient": []}},
      {$project: {OtherIngredient: 0}}
    ])
    

    Playground

    【讨论】:

    • 这个解决方案会比使用 $setIsSubset 更好吗?
    • 可能不会,我总是忘记运营商退出。
    【解决方案2】:

    对不起,我不知道 Golang,但这正是你需要的($function 是新的 MongoDB 4.4):

    let filter = ["Eggs", "Milk", "Butter"];
    
    db.recipes.find({
        $expr: {
            $function: {
                body: function(ingredients, filter) {
    
                    for (let i = 0; i < ingredients.length; i++) {
                        if (filter.indexOf(ingredients[i]) < 0) return false;
                    }
    
                    return true
                },
                args: ["$Ingredients", filter],
                lang: "js"
            }
        }
    });
    

    【讨论】:

      猜你喜欢
      • 2016-09-22
      • 2011-06-23
      • 1970-01-01
      • 2018-12-01
      • 2014-09-28
      • 2011-03-14
      • 2019-08-13
      • 1970-01-01
      • 2017-08-09
      相关资源
      最近更新 更多