简体   繁体   中英

Postgres query to remove duplicates in multiple joined arrays using knex

I'm using knex to build a postgres query and have a table of recipes with a many to many relationship to both a table of ingredients and steps (each step being a part of an instruction). I'm trying to aggregate both the steps and ingredients into their own arrays within the query. My problem is as soon as I join the second array both arrays lose their distinctiveness (ie. table a has 2 elements, table b has 3 elements; after I join table b; both arrays now have 6 elements).

I've tried using distinct but every attempt has resulted in an error being thrown.

Here's what I'm trying to output:

 "id": 1,
  "title": "sometitle",
  "ingredients": [
    {
      "ingredient": "avacado",
      "quantity": 24
    },
    {
      "ingredient": "asparagus",
      "quantity": 42
    },
  ],
  "instructions": [
    {
      "step": 1,
      "instruction": "one"
    },
    {
      "step": 2,
      "instruction": "two"
    },
    {
      "step": 3,
      "instruction": "three"
    },
  ]

Here's what I have so far:

knex(`recipes as r`)
    .where({'r.id': 1})
    .join('ingredients_list as list', {'list.recipe_id': 'r.id'})
    .join('ingredients', {'list.ingredient_id': 'ingredients.id'})
    .join('instructions', {'instructions.recipe_id': 'r.id'})
    .select(
        'r.id',
        db.raw(`json_agg(json_build_object(
            'ingredient', ingredients.name,
            'quantity', list.quantity
            )) as ingredients`),
        db.raw(`json_agg(json_build_object(
            'step', instructions.step_number,
            'instruction', instructions.description
            )) as instructions`)
    )
    .groupBy('r.id')
    .first()

Here's the solution I came up with in case anyone else runs into this issue. I assume this works because postgres is unable to evaluate equality of json objects; whereas jsonb is a binary object. I'd love a more thorough explanation of this is somebody has one.

distinct json_agg(jsonb_build_object(...))
knex(`recipes as r`)
    .where({'r.id': 1})
    .join('ingredients_list as list', {'list.recipe_id': 'r.id'})
    .join('ingredients', {'list.ingredient_id': 'ingredients.id'})
    .join('instructions', {'instructions.recipe_id': 'r.id'})
    .select(
        'r.id',
        db.raw(`distinct json_agg(jsonb_build_object(
            'ingredient', ingredients.name,
            'quantity', list.quantity
            )) as ingredients`),
        db.raw(`distinct json_agg(jsonb_build_object(
            'step', instructions.step_number,
            'instruction', instructions.description
            )) as instructions`)
    )
    .groupBy('r.id')
    .first()

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