代码之家  ›  专栏  ›  技术社区  ›  Alexander Farber

将限制添加到ARRAY\u to\u JSON或ARRAY\u AGG

  •  2
  • Alexander Farber  · 技术社区  · 7 年前

    在一个以PostgreSQL 9.6.6为后端的双人游戏中,我定义了以下自定义存储函数,用于检索用户的聊天信息:

    CREATE OR REPLACE FUNCTION words_get_user_chat(
                    in_uid integer
            ) RETURNS jsonb AS
    $func$
            SELECT COALESCE(
                    JSONB_OBJECT_AGG(gid, ARRAY_TO_JSON(y)),
                    '{}'::jsonb
            ) FROM (
                    SELECT  c.gid,
                            ARRAY_AGG(
                                    JSON_BUILD_OBJECT(
                                            'created', EXTRACT(EPOCH FROM c.created)::int,
                                            'uid',     c.uid,
                                            'msg',     c.msg
                                    )
                                    ORDER BY c.created ASC
                            ) AS y
                    FROM      words_chat c
                    LEFT JOIN words_games g
                    USING     (gid)
                    WHERE     in_uid in (g.player1, g.player2)
                    AND       (g.finished IS NULL OR g.finished > CURRENT_TIMESTAMP - INTERVAL '1 day')
                    GROUP BY  c.gid
                    /* LIMIT 10 */
            ) AS x;
    
    $func$ LANGUAGE sql STABLE;
    

    它连接words\u游戏和words\u聊天表,并生成以下JSON对象(游戏id为“9”作为字符串键),其中包含一个包含消息的JSON数组:

     # select words_get_user_chat(6);
    
    
    
                                                                                                                       words_get_user_chat                        
    
    
    
    
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    --------------------------------------------------------------------------------------------------------------------------------------------------------------
    -------------------------------------------------------------------------------------------
     {"9": [{"msg": "test2", "uid": 6, "created": 1516445342}, {"msg": "test3", "uid": 6, "created": 1516445358}, {"msg": "test4", "uid": 6, "created": 1516445369
    }, {"msg": "test5", "uid": 6, "created": 1516445381}, {"msg": "test6", "uid": 6, "created": 1516445405}, {"msg": "test7", "uid": 6, "created": 1516445415}, {"
    msg": "test8", "uid": 6, "created": 1516445508}, {"msg": "test9", "uid": 6, "created": 1516445539}, {"msg": "test10", "uid": 6, "created": 1516445743}, {"msg"
    : "test11", "uid": 6, "created": 1516445989}, {"msg": "test12", "uid": 6, "created": 1516446101}, {"msg": "test13", "uid": 6, "created": 1516446125}, {"msg": 
    "test14", "uid": 6, "created": 1516446145}, {"msg": "test15", "uid": 6, "created": 1516446227}, {"msg": "test16", "uid": 6, "created": 1516446652}, {"msg": "t
    est17", "uid": 6, "created": 1516446999}, {"msg": "test18", "uid": 6, "created": 1516447168}, {"msg": "test19", "uid": 6, "created": 1516447229}, {"msg": "tes
    t20", "uid": 6, "created": 1516447493}, {"msg": "test21", "uid": 6, "created": 1516447532}, {"msg": "test22", "uid": 6, "created": 1516447555}, {"msg": "test2
    3", "uid": 6, "created": 1516448017}, {"msg": "test24", "uid": 6, "created": 1516448062}]}
    (1 row)
    

    这很有效,但我想补充一点 LIMIT 10 数组元素的数量-作为防止聊天泛滥的措施。

    我曾尝试将其添加到函数中(请参见上面的注释行),但没有效果。

    你能给我建议一下正确的旅游地点吗 限制10 ?

    我通过WebSockets将JSON对象发送到Android应用程序,并希望防止恶意用户通过泛滥聊天来破坏这些对象的大小。

    更新:

    我正在尝试迈克的建议:

    CREATE OR REPLACE FUNCTION words_get_user_chat(
                    in_uid integer
            ) RETURNS jsonb AS
    $func$
            SELECT COALESCE(
                    JSONB_OBJECT_AGG(gid, ARRAY_TO_JSON(y)),
                    '{}'::jsonb
            ) FROM (
                    SELECT  c.gid,
                            ROW_NUMBER() OVER (PARTITION BY c.gid) AS rn,
                            ARRAY_AGG(
                                    JSON_BUILD_OBJECT(
                                            'created', EXTRACT(EPOCH FROM c.created)::int,
                                            'uid',     c.uid,
                                            'msg',     c.msg
                                    )
                                    ORDER BY c.created ASC
                            ) AS y
                    FROM      words_chat c
                    LEFT JOIN words_games g
                    USING     (gid)
                    WHERE     in_uid in (g.player1, g.player2)
                    AND       (g.finished IS NULL OR g.finished > CURRENT_TIMESTAMP - INTERVAL '1 day')
                    AND       rn < 10
                    GROUP BY  c.gid
            ) AS x;
    
    $func$ LANGUAGE sql STABLE;
    

    但不幸的是,出现语法错误:

    ERROR:  42703: column "rn" does not exist
    LINE 24:                 AND       rn < 10
                                       ^
    LOCATION:  errorMissingColumn, parse_relation.c:3194
    

    更新2:

    这是我使用的两个表,很抱歉之前没有包括这些信息-

    #\d words_chat
                                       Table "public.words_chat"
     Column  |           Type           |                        Modifiers                         
    ---------+--------------------------+----------------------------------------------------------
     cid     | bigint                   | not null default nextval('words_chat_cid_seq'::regclass)
     created | timestamp with time zone | not null
     gid     | integer                  | not null
     uid     | integer                  | not null
     msg     | text                     | not null
    Indexes:
        "words_chat_pkey" PRIMARY KEY, btree (cid)
    Foreign-key constraints:
        "words_chat_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
        "words_chat_uid_fkey" FOREIGN KEY (uid) REFERENCES words_users(uid) ON DELETE CASCADE
    
    # \d words_games
                                       Table "public.words_games"
      Column  |           Type           |                         Modifiers                         
    ----------+--------------------------+-----------------------------------------------------------
     gid      | integer                  | not null default nextval('words_games_gid_seq'::regclass)
     created  | timestamp with time zone | not null
     finished | timestamp with time zone | 
     player1  | integer                  | not null
     player2  | integer                  | 
     played1  | timestamp with time zone | 
     played2  | timestamp with time zone | 
     state1   | text                     | 
     state2   | text                     | 
     hint1    | text                     | 
     hint2    | text                     | 
     score1   | integer                  | not null
     score2   | integer                  | not null
     hand1    | character(1)[]           | not null
     hand2    | character(1)[]           | not null
     pile     | character(1)[]           | not null
     letters  | character(1)[]           | not null
     values   | integer[]                | not null
     bid      | integer                  | not null
    Indexes:
        "words_games_pkey" PRIMARY KEY, btree (gid)
    Check constraints:
        "words_games_check" CHECK (player1 <> player2)
        "words_games_score1_check" CHECK (score1 >= 0)
        "words_games_score2_check" CHECK (score2 >= 0)
    Foreign-key constraints:
        "words_games_bid_fkey" FOREIGN KEY (bid) REFERENCES words_boards(bid) ON DELETE CASCADE
        "words_games_player1_fkey" FOREIGN KEY (player1) REFERENCES words_users(uid) ON DELETE CASCADE
        "words_games_player2_fkey" FOREIGN KEY (player2) REFERENCES words_users(uid) ON DELETE CASCADE
    Referenced by:
        TABLE "words_chat" CONSTRAINT "words_chat_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
        TABLE "words_moves" CONSTRAINT "words_moves_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
        TABLE "words_scores" CONSTRAINT "words_scores_gid_fkey" FOREIGN KEY (gid) REFERENCES words_games(gid) ON DELETE CASCADE
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   Erwin Brandstetter    2 年前

    您的函数应如下所示:

    CREATE OR REPLACE FUNCTION words_get_user_chat(in_uid integer)
      RETURNS jsonb AS
      LANGUAGE sql STABLE
    $func$
    SELECT COALESCE(jsonb_object_agg(gid, y), '{}')
    FROM  (
       SELECT gid, jsonb_agg((SELECT j FROM (SELECT created, uid, msg) j)) AS y
       FROM  (
          SELECT DISTINCT gid  -- DISTINCT may be redundant
          FROM   words_games
          WHERE (finished IS NULL
              OR finished > (CURRENT_TIMESTAMP - INTERVAL '1 day'))
          AND    in_uid IN (player1, player2)
          ) g
       CROSS JOIN LATERAL (
          SELECT EXTRACT(EPOCH FROM created)::int AS created
               , uid
               , msg
          FROM   words_chat c
          WHERE  c.gid = g.gid
          ORDER  BY c.created DESC
          LIMIT  10                        --  HERE !!
          ) c 
       GROUP  BY 1
       ) x
    $func$;
    

    不要聚合所有行,只是为了稍后丢弃多余的行。那将是一种浪费。放置 LIMIT 之后 ORDER BY 在子查询中。

    您需要确定资格 gid 从…起 words_games 首先进行此操作,然后使用 LATERAL 连接到上的子查询 words_chat . 也应该是正确的和更快的。

    自从 c.created 已定义 NOT NULL ,无需添加 NULLS LAST 订购人 条款此匹配的多列索引应产生最佳读取性能:

    CREATE INDEX ON words_chat(gid, created DESC);
    

    也许还有一些索引 words\u游戏 . 取决于基数和值频率。

    同时,我还简化了结构 jsonb 后果

    相关:

        2
  •  2
  •   5ar    7 年前

    大概 this 回答您的问题。尝试执行以下操作:

    (ARRAY_AGG( /* same as before */ ))[1:10] AS y