Sắp xếp dữ liệu người dùng

Một phần của tài liệu Xây dựng hệ quản trị đào tạo tích hợp mạng xã hội (Trang 124 - 142)

5 Phân tích và thiết kế hệ thống

6.3 Sắp xếp dữ liệu người dùng

Vì hệ thống tập trung vào trải nghiệm của người học là chính. Việc sắp xếp dữ liệu người dùng (user-generated contents) là tối quan trọng để đảm bảo trải nghiệm của người dùng. Đối với mỗi loại dữ liệu, nhóm làm đề tài lại có áp dụng và thử nghiệm một số phương pháp và giải thuật khác nhau.

Đối với các bài viết trong một nhóm thảo luận, nhóm làm đề tài sử dụng cơng thức sau để sắp xếp:

sign(v)×log(max(|v|,1))+

d

1000⌋−pivot

45000

với:

v là tổng số upvote trừ cho tổng số downvote của bài viết.

d là ngày bài viết được tạo (là giá trị thời gian UNIX tính bằng đơn vị milliseconds).

Nhóm làm đề tài chia giá trị này cho 1000 để ra được đơn vị seconds.  pivot là thời điểm bất kỳ nhỏ hơn mọi giá trị d trong hệ thống.

Cơng thức trên có là tổng của hai số hạng. Số hạng thứ nhất chỉ ra rằng, một bài viết có nhiều lượt vote tích cực hơn số lượng vote tiêu cực thì sẽ được đẩy lên đầu. Hàm số log có mặt để đảm bảo rằng việc tăng lượt vote tích cực tuân theo quy luật hiệu suất giảm dần (diminishing returns). Nhóm làm đề tài muốn lượt vote phải đóng góp vào thứ hạng của bài viết, nhưng cũng muốn là nó khơng lấn át q nhiều số hạng thứ hai: sự hao mòn theo thời gian (time decay). Sự hao mòn theo thời gian được tính bằng khoảng thời gian giữa ngày tạo bài viết và một giá trị gốc nào đó (tính bằng giây) chia cho 45000s. Ý nghĩa của cả cơng thức nói lên rằng: một bài viết có v là 100 sẽ có được xếp ngang hàng với một bài viết có v là 1000 nhưng được tạo vào 12 tiếng trước. Sở dĩ chúng ta khơng tính sự hao mịn bằng cách lấy thời điểm hiện tại trừ cho thời gian tạo là bởi khi đó, ta phải tính tốn lại cơng thức mỗi khi query vào cơ sở dữ liệu. Sử dụng công thức trên cho phép chúng ta phi chuẩn hóa, lưu trữ một trường hotness trong bài viết và chỉ tính tốn lại khi có lượt vote mới vì số hạng thứ hai đã được cố định. Ngồi cơng thức trên, bài viết cũng cho phép sắp xếp theo thời gian và theo tổng lượt vote (không bị ảnh hưởng bởi thời gian).

Đối với các bình luận ở bài viết, nhóm làm đề tài sử dụng cơng thức sau để sắp xếp:

^

p+zα2±zα2√[ ^p(1− ^p)+zα2/4n]/n

1+zα2/n

Cơng thức trên chính là giới hạn dưới của khoảng tin cậy điểm Wilson (Wilson Score

Confidence Interval). Giả sử rằng các lượt vote ở một bài bình luận (bao gồm vote tiêu cực và

vote tích cực) đại diện cho thích và khơng thích là một mẫu lấy từ tồn bộ dân số là số lượng người trong nhóm. Khi đó, việc sử dụng cơng thức trên có ý nghĩa là: với mẫu vote có được ở bình luận, thì với độ tin cậy là 95%, tỉ lệ thực sự các số lượng người thích trên tổng số người dùng trong nhóm ít nhất là bao nhiêu? Các kí hiệu trong cơng thức trên là:

 ^p là tỉ lệ số lượng vote tích cực trên tổng số lượng vote. Đây cũng chính là tỉ lệ của tham số mà ta cần tính khoảng tin cậy ở mẫu.

n là tổng số lượng vote (cũng chính là số lượng dữ liệu ở mẫu).

zα là z-score, thường dùng là 1.96 với độ tin cậy là 95%.

Hệ thống sử dụng cơng thức trên để sắp xếp bình luận để đảm bảo các bình luận cần phải có tỉ lệ vote tích cực trên vote tiêu cực lớn. Chúng ta không sử dụng tỉ lệ này trực tiếp làm công thức sắp xếp là bởi nếu như vậy thì các bình luận có ít vote tích cực nhưng tỉ lệ cao sẽ được sắp xếp lên trước các bình luận có nhiều vote tích cực nhưng tỉ lệ thấp hơn đơi chút. Điều này là sai bởi đối với các bình luận có lượng vote ít, thì tỉ lệ này sẽ rất ngẫu nhiên và khơng có khả năng phản ánh lên quan điểm của tồn bộ người dùng trong nhóm về bình luận. Hiện tượng cịn có tên là lời nguyền của kích thước mẫu nhỏ (the curse of small sample size).

Hệ thống không sử dụng công thức Wilson cho bài viết là bởi vì cơng thức này khơng thể đưa vào hiệu ứng suy giảm thời gian. Ảnh hưởng của thời gian đối với bài viết là quan trọng vì hệ thống muốn người dùng ln được tiếp cận với các bài viết mới nhất có thể.

Hệ thống cũng thực hiện việc sắp xếp nhóm mà người dùng đã tham gia và bạn bè của họ. Việc sắp xếp này xảy ra xuyên suốt trong các thành phần: ở hệ thống tìm kiếm (đẩy kết quả bạn bè lên đầu, nhóm mà mình đã tham gia lên đầu), hiển thị nhóm đã tham gia, hiển thị thành viên trong các nhóm được giới thiệu (sắp xếp bạn bè lên đầu), v.v… Hệ thống sắp xếp các nội dung này bằng việc giới thiệu một chỉ số có tên là interactionScore. Chỉ số này thu thập được từ tương tác của người dùng đến nhóm và bạn bè. Đối với nhóm, đó có thể là tạo bài viết, tạo bình luận, tạo vote. Đối với bạn bè, đó có thể là nhắn tin, xem trang thơng tin cá nhân, bình luận hoặc vote vào bài viết, bình luận của nhau. Hệ thống thu thập các tương tác này và tính tổng theo từng ngày (thông qua cronjob chạy cho từng ngày) và cập nhật

interactionScore dựa theo công thức sau:

score=∑ di wiCie−λ(d0−d) với:  i là một loại tương tác

Ci là số lượng của loại tương tác i trong một ngày

wi là trọng số của tương tác i hay mức độ quan trọng của tương tác i đến score cuối cùng.

d là ngày tương tác i được tạo.

e−λ(d0−d) là hệ số phân ra theo cấp số nhân của thời gian. Hệ số lambda sẽ được chọn sao cho sau 30 ngày thì hệ số này gần như bằng 0.

Nói cách khác, interactionScore là tổng phân ra theo thời gian của tổng có trọng số của các tương tác theo từng ngày.

Một vấn đề liên quan đến sắp xếp dữ liệu mà bất kỳ hệ thống nào cũng phải quan tâm đó chính là phân trang. Cách phân trang truyền thống được sử dụng rộng rãi đó là:

Hình 35: Request body trong trườnghợp phân trang truyền thống hợp phân trang truyền thống Khi đó, câu SQL query sẽ có dạng như sau:

Hình 36: Câu lệnh query sử dụng phân trangtruyền thống truyền thống

Tuy nhiên, cách làm trên sẽ không hợp lý trong trường hợp phân trang các dữ liệu mà thứ tự sắp xếp có thể thay đổi liên tục, ví dụ như bài viết và bình luận đã được trình bày ở trên. Thứ tự bài viết có thể thay đổi liên tục tùy vào lượt vote, do đó, khi người dùng xem các bài viết trong một nhóm và chuyển trang thì có thể gặp trường hợp một bài viết bị lặp lại do thứ tự của nó bị đẩy xuống dưới.

Hình 37: Request Body trong trường hợp phântrang con trỏ. trang con trỏ.

Cách phân trang này có tên là phân trang con trỏ (cursor-based pagination). Thay thế cho

page, chúng ta sử dụng anchor (hay cursor) là một param truyền vào và cũng là đối tượng trả

ra của API. Khi client query vào API này, đối tượng trả ra sẽ chứa anchor được tính tốn như sau: trong danh sách các kết quả trả về, lấy ở kết quả cuối cùng giá trị dùng để sắp xếp và id của nó rồi ghép lại bằng một ký tự separator nào đó. Khi client muốn lấy thêm một danh sách kết quả khác có thứ tự thấp hơn kết quả trước, anchor trả ra trước đó sẽ được gửi kèm theo. Hệ thống sử dụng anchor này cho câu lệnh SQL như sau:

Hình 38: Câu lệnh query sử dụng phân trang con trỏ

Cách phân trang này sẽ giải quyết được vấn đề lặp lại khi xem nội dung có thứ tự thay đổi liên tục.

6.4 Tìm kiếm

6.4.1 Xây dựng index

Hệ thống sử dụng ElasticSearch để hiện thực các chức năng tìm kiếm. Ở ElasticSearch, đơn vị tương ứng với bảng của SQL là một index. Cũng tương tự như bảng ở SQL, để tạo một index, ta cần định nghĩa các trường dữ liệu (gọi là mapping) và các siêu dữ liệu (còn gọi là

settings) mơ tả các đặc tính của index đó. Các trường dữ liệu thường có kiểu mapping gần như

1 – 1 với kiểu dữ liệu của SQL, với một số lưu ý sau:

 Đối với kiểu dữ liệu dạng số và dạng ngày, ElasticSearch có kiểu tương ứng: integer,

 Đối với kiểu dữ liệu là chuỗi, ta có hai lựa chọn: text và keyword. Điểm khác biệt là ở chỗ keyword chỉ cho phép tìm kiếm chính xác (cịn gọi là term query), cịn text cho phép tìm kiếm dựa trên siêu thơng tin analyzer được gán cho biến đó (gọi là match

query). Hệ thống sử dụng kiểu text đối với các trường mà người dùng muốn tìm kiếm

kết quả dựa trên: ví dụ như đối với khóa học thì là tên và thơng tin mơ tả khóa học. Hệ thống cũng sử dụng kiểu keyword cho những nghiệp vụ: lọc kết quả (ví dụ lọc khóa học theo thể loại của khóa học và sắp xếp dữ liệu (sắp xếp khóa học theo tên của khóa học).

 Một trường có thể thuộc nhiều kiểu dữ liệu khác nhau để phục vụ mục đích khác nhau, gọi là multi-fields. Ở hình dưới, nhóm làm đề tài định nghĩa title của người dùng được lưu trữ cùng kiểu text nhưng có hai analyzer khác nhau. Một ví dụ khác có thể kể đến là tên của người dùng vừa có thể được index ở dạng text phục vụ tìm kiếm, vừa ở dạng keyword để phục vụ việc sắp xếp.

Hình 39: Một lát cắt đặc tả mapping của index user

 Trường dữ liệu có thể tùy chọn khơng index (bằng cách tùy chọn index là false như ở hình trên). Mặc dù trường này vẫn được lưu trữ và trả ra trong kết quả tìm kiếm, ElasticSeach sẽ khơng tạo index ngược cho trường này. Việc này rất hữu ích để hệ thống thực hiện việc lưu trữ thêm các thông tin khác của thực thể vào index để khơng phải query vào cơ sở dữ liệu chính giảm hiệu năng, nhưng vẫn đảm bảo rằng việc index các thông tin này vào Elasticsearch vẫn nhanh chóng (vì khơng cần phải xây

index ngược).

 Mọi kiểu dữ liệu trong ElasticSearch đều có thể chứa một danh sách giá trị. Do đó, trong ES khơng hề có kiểu array.

 Có thể lưu trữ đối tượng lồng nhau (nested object). Hệ thống sử dụng kiểu dữ liệu này để lưu trữ danh sách thực thể có liên quan với thực thể chính. Vì trong ElasticSearch khơng hề có khái niệm Quan hệ, đây là giải pháp để hiện thực quan hệ 1 – N và quan hệ M – N. Ví dụ ở hình trên, hệ thống lưu trữ những người dùng là bạn của người dùng chính. Đây là mối quan hệ M – N, do đó cần phải tạo kiểu dữ liệu nested. Hệ thống chỉ sử dụng kiểu dữ liệu nested này cho mục đích lọc và sắp xếp dữ liệu. Cũng là ví dụ bạn bè trên, với mapping này chúng ta có thể hỗ trợ tìm kiếm lọc những người có tên phù hợp với từ khóa và có id của friends trùng với id của người dùng đang đăng nhập Mặc định các câu query trong ES đều có tốn tử là OR, nên chỉ cần một phần tử trong danh sách nested objects này trùng thì sẽ tính là match. Có thể thấy nhược điểm của cách làm này đó chính là đánh đổi về bộ nhớ. Giả sử ta có mối quan hệ M – N, thì ta chỉ mất ít nhất M + N + (M × N) bộ nhớ khi lưu ở SQL, trong khi đó đối với kiểu nested thì sẽ là M + N + 2(M × N) (giả sử ta lưu nested ở cả hai chiều). Ngồi ra, một

nhược điểm nữa đó là ở hiệu năng và độ phức tạp khi thêm, xóa, sửa quan hệ. Khi xóa bạn bè chẳng hạn, ta cần phải tìm trong danh sách đối tượng nested friends và xóa kết quả trùng id đi. Tác vụ này có độ phức tạp là O(N) so với O(1) trong trường hợp SQL. Ngoài kiểu dữ liệu, tại thời điểm tạo index, chúng ta cũng cần phải xác định thêm bộ

analyzer được sử dụng cho các trường dữ liệu đó. Analyzer được sử dụng để chuyển đổi văn

bản khơng có cấu trúc (tên người dùng, mơ tả khóa học) thành dạng có cấu trúc tối ưu cho việc tìm kiếm. Cấu trúc của một analyzer gồm ba phần:

1. char_filter: Bộ lọc ký tự tiền xử lý trước khi đưa vào tokenizer, ví dụ html_strip dùng

để loại bỏ các phần tử HTML khỏi đoạn văn.

2. tokenizer: chuyển đổi dạng ký tự thô nhận được thành các token để đánh index ngược.

Một số tokenizer phổ biến: standard chia văn bản thành từ dựa trên ký tự phân cách từ (ví dụ như khoảng trắng) và loại bỏ các dấu chấm câu – đây cũng là tokenizer mặc định cho trường dạng text; n-gram chia văn bản thành các từ và trả về chuỗi n ký tự liên tục của mỗi từ đó (ví dụ 2-gram của math sẽ là ma, at, th); keyword tokenizer trả về đúng văn bản nhận vào – đây là tokenizer mặc định cho trường dạng keyword.

3. filter: nhận vào danh sách các token và trả ra danh sách token mới được biến đổi theo

một cách nào đó. Một số filter tiêu biểu: lowercase dùng để chuyển thành chữ thường (quan trọng vì tìm kiếm thường phải khơng phân biệt chữ hoa và chữ thường); ascii- folding: chuyển đổi ký tự unicode thành dạng ASCII tương ứng (ví dụ ă thành a).

ElasticSearch cung cấp sẵn một analyzer chuẩn tắc (standard) bao gồm standard

tokenizer và lowercase filter. Analyzer đáp ứng gần như đầy đủ nhu cầu sử dụng. Tuy vậy,

trong quá trình, hệ thống phát sinh một số use-case mà nhóm làm đề tài cần phải mở rộng thêm từ analyzer này như sau:

 Tìm kiếm tiếng việt. Hiện tại, hệ thống hỗ trợ tìm kiếm tiếng việt thơng qua dạng ASCII tương ứng của nó. Ví dụ, cụm từ “tốn học” sẽ được quy thành “toan hoc” tại thời điểm đánh index và thời điểm truy vấn. Nhóm làm đề tài quyết định sử dụng filter

 Tìm kiếm các đoạn văn bản có nhập thẻ HTML. Đối với những trường dữ liệu như mơ tả khóa học,… hệ thống cho phép nhập văn bản dạng rich text, tức là có thể chứa các định dạng văn bản dưới dạng thẻ HTML. Nhóm làm đề tài quyết định sử dụng

char_filter html_strip để hiên thực tính năng này.

 Tìm kiếm từng phần (partial match). Đối với các đoạn văn bản ngắn như tên người dùng, tên nhóm và tên khóa học thì tìm kiếm full-text khơng thơi là rất hạn chế. Chúng ta cần hỗ trợ tìm kiếm từng phần (ví dụ như nhập Qua thì có thể ra được kết quả Quân chẳng hạn). Để hỗ trợ tính năng này, hệ thống sử dụng filter edge-ngram. Edge-ngram gần giống với n-gram, khác biệt nằm ở chỗ các n-gram phải chứa ký tự bắt đầu của từng token. Với văn bản “con mèo”, filter này sẽ sinh ra các 2-gram là co và me. Filter này cho phép định nghĩa min-gram và max-gram, tương ứng với số n ít nhất và nhiều nhất filter này có thể sinh ra các n-gram. Filter này cũng được nhóm làm đề tài sử dụng để hiện thực tính năng search-as-you-type giúp tăng trải nghiệm tìm kiếm của người dùng. Ở ví dụ của hình dưới, nhóm làm đề tài định nghĩa hai analyzer,

partial_analyzer dùng để đánh index trường dữ liệu cần tìm kiếm một phần và partial_search dùng để biến đổi kết quả tìm kiếm. Nhắc lại, mặc định văn bản sẽ được

biến đổi sử dụng cùng 1 loại analyzer khi đánh index và khi tìm kiếm. Ở đây nhóm làm đề tài định nghĩa hai loại riêng biệt sử dụng cho hai trường hợp vì khi tìm kiếm thì chúng ta khơng cần phải biến đổi kết quả này thành n-gram như khi index.

Hình 41: Ví dụ sử dụng các analyzer ở trên để phục vụ mục đích tìmkiếm khác nhau kiếm khác nhau

6.4.2 Đồng bộ dữ liệu

Vì ElasticSearch là một cơ sở dữ liệu phụ bên cạnh cơ sở dữ liệu chính, việc đồng bộ dữ liệu hết sức quan trọng.

Hệ thống sử dụng các API sau của ElasticSearch để hiện thực việc đồng bộ dữ liệu từ cơ sở dữ liệu Postgres:

 Bulk API: Được sử dụng để làm mới hoàn toàn dữ liệu từ Postgres vào ElasticSearch. Mỗi ngày, toàn bộ dữ liệu trong ES sẽ được xóa và được làm mới hồn tồn để loại bỏ

Một phần của tài liệu Xây dựng hệ quản trị đào tạo tích hợp mạng xã hội (Trang 124 - 142)

Tải bản đầy đủ (PDF)

(185 trang)