مناسب راستے تلاش کرنے کے مسئلے کو حل کرنے کے لیے JanusGraph گراف DBMS کے قابل اطلاق کی جانچ کرنے والا ایک تجربہ

مناسب راستے تلاش کرنے کے مسئلے کو حل کرنے کے لیے JanusGraph گراف DBMS کے قابل اطلاق کی جانچ کرنے والا ایک تجربہ

سب کو سلام. ہم آف لائن ٹریفک تجزیہ کے لیے ایک پروڈکٹ تیار کر رہے ہیں۔ پراجیکٹ کے پاس تمام خطوں کے زائرین کے راستوں کے شماریاتی تجزیہ سے متعلق کام ہے۔

اس کام کے حصے کے طور پر، صارفین درج ذیل قسم کے سسٹم کے سوالات پوچھ سکتے ہیں:

  • کتنے زائرین علاقے "A" سے علاقے "B" تک گئے؟
  • کتنے زائرین علاقے "A" سے علاقے "B" سے علاقے "C" اور پھر علاقے "D" سے گزرے؟
  • ایک مخصوص قسم کے وزیٹر کو علاقے "A" سے علاقے "B" تک جانے میں کتنا وقت لگا۔

اور اسی طرح کے متعدد تجزیاتی سوالات۔

تمام علاقوں میں زائرین کی نقل و حرکت ایک ہدایت شدہ گراف ہے۔ انٹرنیٹ کو پڑھنے کے بعد، میں نے دریافت کیا کہ گراف DBMSs کو تجزیاتی رپورٹس کے لیے بھی استعمال کیا جاتا ہے۔ مجھے یہ دیکھنے کی خواہش تھی کہ گراف DBMSs اس طرح کے سوالات سے کیسے نمٹیں گے (TL؛ DR؛ خراب)۔

میں نے DBMS استعمال کرنے کا انتخاب کیا۔ جینس گراف، گراف اوپن سورس DBMS کے ایک شاندار نمائندے کے طور پر، جو بالغ ٹیکنالوجیز کے اسٹیک پر انحصار کرتا ہے، جو (میری رائے میں) اسے مہذب آپریشنل خصوصیات کے ساتھ فراہم کرنا چاہئے:

  • برکلے ڈی بی اسٹوریج بیک اینڈ، اپاچی کیسینڈرا، سکیلا؛
  • پیچیدہ اشاریہ جات کو Lucene، Elasticsearch، Solr میں محفوظ کیا جا سکتا ہے۔

JanusGraph کے مصنفین لکھتے ہیں کہ یہ OLTP اور OLAP دونوں کے لیے موزوں ہے۔

میں نے BerkeleyDB، Apache Cassandra، Scylla اور ES کے ساتھ کام کیا ہے، اور یہ مصنوعات اکثر ہمارے سسٹمز میں استعمال ہوتی ہیں، اس لیے میں اس گراف DBMS کو جانچنے کے بارے میں پر امید تھا۔ مجھے RocksDB پر BerkeleyDB کا انتخاب کرنا عجیب لگا، لیکن یہ شاید لین دین کی ضروریات کی وجہ سے ہے۔ کسی بھی صورت میں، قابل توسیع، مصنوعات کے استعمال کے لیے، یہ تجویز کیا جاتا ہے کہ کیسینڈرا یا سکیلا پر بیک اینڈ استعمال کریں۔

میں نے Neo4j پر غور نہیں کیا کیونکہ کلسٹرنگ کے لیے تجارتی ورژن کی ضرورت ہوتی ہے، یعنی پروڈکٹ اوپن سورس نہیں ہے۔

گراف ڈی بی ایم ایس کہتے ہیں: "اگر یہ گراف کی طرح لگتا ہے، تو اسے گراف کی طرح سمجھیں!" - خوبصورتی!

سب سے پہلے، میں نے ایک گراف کھینچا، جو بالکل گراف DBMSs کے اصولوں کے مطابق بنایا گیا تھا:

مناسب راستے تلاش کرنے کے مسئلے کو حل کرنے کے لیے JanusGraph گراف DBMS کے قابل اطلاق کی جانچ کرنے والا ایک تجربہ

ایک جوہر ہے۔ Zoneعلاقے کے لیے ذمہ دار۔ اگر ZoneStep اس سے تعلق رکھتا ہے Zone، پھر وہ اس کی طرف اشارہ کرتا ہے۔ جوہر پر Area, ZoneTrack, Person توجہ نہ دیں، ان کا تعلق ڈومین سے ہے اور انہیں ٹیسٹ کا حصہ نہیں سمجھا جاتا ہے۔ مجموعی طور پر، اس طرح کے گراف ڈھانچے کے لیے ایک سلسلہ کی تلاش کا سوال اس طرح نظر آئے گا:

g.V().hasLabel('Zone').has('id',0).in_()
       .repeat(__.out()).until(__.out().hasLabel('Zone').has('id',19)).count().next()

روسی زبان میں کیا کچھ اس طرح ہے: ID=0 کے ساتھ ایک زون تلاش کریں، ان تمام چوٹیوں کو لیں جہاں سے کوئی کنارہ اس تک جاتا ہے (ZoneStep)، پیچھے ہٹے بغیر اس وقت تک روکیں جب تک کہ آپ کو وہ ZoneSteps نہ مل جائیں جہاں سے زون کا ایک کنارہ ہے۔ ID=19، ایسی زنجیروں کی تعداد شمار کریں۔

میں گرافس پر تلاش کی تمام پیچیدگیوں کو جاننے کا بہانہ نہیں کرتا، لیکن یہ سوال اس کتاب کی بنیاد پر تیار کیا گیا تھا (https://kelvinlawrence.net/book/Gremlin-Graph-Guide.html).

میں نے BerkeleyDB بیک اینڈ کا استعمال کرتے ہوئے JanusGraph گراف ڈیٹا بیس میں 50 سے 3 پوائنٹس کی لمبائی کے 20 ہزار ٹریک لوڈ کیے، اس کے مطابق انڈیکس بنائے قیادت.

ازگر ڈاؤن لوڈ اسکرپٹ:


from random import random
from time import time

from init import g, graph

if __name__ == '__main__':

    points = []
    max_zones = 19
    zcache = dict()
    for i in range(0, max_zones + 1):
        zcache[i] = g.addV('Zone').property('id', i).next()

    startZ = zcache[0]
    endZ = zcache[max_zones]

    for i in range(0, 10000):

        if not i % 100:
            print(i)

        start = g.addV('ZoneStep').property('time', int(time())).next()
        g.V(start).addE('belongs').to(startZ).iterate()

        while True:
            pt = g.addV('ZoneStep').property('time', int(time())).next()
            end_chain = random()
            if end_chain < 0.3:
                g.V(pt).addE('belongs').to(endZ).iterate()
                g.V(start).addE('goes').to(pt).iterate()
                break
            else:
                zone_id = int(random() * max_zones)
                g.V(pt).addE('belongs').to(zcache[zone_id]).iterate()
                g.V(start).addE('goes').to(pt).iterate()

            start = pt

    count = g.V().count().next()
    print(count)

ہم نے ایس ایس ڈی پر 4 کور اور 16 جی بی ریم والا VM استعمال کیا۔ JanusGraph اس کمانڈ کا استعمال کرتے ہوئے تعینات کیا گیا تھا:

docker run --name janusgraph -p8182:8182 janusgraph/janusgraph:latest

اس صورت میں، ڈیٹا اور اشاریہ جات جو عین مطابق مماثلت کی تلاش کے لیے استعمال ہوتے ہیں برکلے ڈی بی میں محفوظ کیے جاتے ہیں۔ پہلے دی گئی درخواست پر عمل کرنے کے بعد، مجھے کئی دسیوں سیکنڈ کے برابر وقت ملا۔

مندرجہ بالا 4 اسکرپٹس کو متوازی طور پر چلا کر، میں نے DBMS کو جاوا اسٹیک ٹریسز (اور ہم سب کو جاوا اسٹیک ٹریسز کو پڑھنا پسند ہے) کے ساتھ ایک کدو میں تبدیل کیا

کچھ سوچنے کے بعد، میں نے گراف ڈایاگرام کو درج ذیل تک آسان بنانے کا فیصلہ کیا:

مناسب راستے تلاش کرنے کے مسئلے کو حل کرنے کے لیے JanusGraph گراف DBMS کے قابل اطلاق کی جانچ کرنے والا ایک تجربہ

یہ فیصلہ کرنا کہ ہستی کے اوصاف کے ذریعے تلاش کرنا کناروں سے تلاش کرنے سے تیز تر ہوگا۔ نتیجے کے طور پر، میری درخواست درج ذیل میں بدل گئی:

g.V().hasLabel('ZoneStep').has('id',0).repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',19)).count().next()

روسی میں کیا کچھ اس طرح ہے: ID=0 کے ساتھ ZoneStep کو تلاش کریں، اس وقت تک پیچھے نہ جائیں جب تک کہ آپ ID=19 کے ساتھ ZoneStep تلاش نہ کریں، ایسی زنجیروں کی تعداد گنیں۔

میں نے اوپر دیے گئے لوڈنگ اسکرپٹ کو بھی آسان بنایا تاکہ غیر ضروری کنکشنز نہ بنیں، خود کو اوصاف تک محدود رکھتے ہوئے

درخواست کو مکمل ہونے میں ابھی بھی کئی سیکنڈ لگے، جو ہمارے کام کے لیے مکمل طور پر ناقابل قبول تھا، کیونکہ یہ کسی بھی قسم کی AdHoc درخواستوں کے مقاصد کے لیے بالکل موزوں نہیں تھی۔

میں نے JanusGraph کو Scylla کا استعمال کرتے ہوئے Cassandra کے تیز ترین نفاذ کے طور پر تعینات کرنے کی کوشش کی، لیکن اس سے کارکردگی میں کوئی خاص تبدیلی نہیں آئی۔

لہذا اس حقیقت کے باوجود کہ "یہ ایک گراف کی طرح لگتا ہے"، میں اس پر تیزی سے کارروائی کرنے کے لیے گراف DBMS حاصل نہیں کر سکا۔ میں مکمل طور پر فرض کرتا ہوں کہ ایسی کوئی چیز ہے جس کے بارے میں میں نہیں جانتا ہوں اور یہ کہ JanusGraph کو سیکنڈ کے ایک حصے میں اس تلاش کو انجام دینے کے لیے بنایا جا سکتا ہے، تاہم، میں ایسا کرنے کے قابل نہیں تھا۔

چونکہ اس مسئلے کو ابھی بھی حل کرنے کی ضرورت ہے، اس لیے میں نے جدولوں کے JOINs اور Pivots کے بارے میں سوچنا شروع کیا، جو خوبصورتی کے لحاظ سے رجائیت کو متاثر نہیں کرتے تھے، لیکن عملی طور پر ایک مکمل طور پر قابل عمل آپشن ہو سکتا ہے۔

ہمارا پروجیکٹ پہلے سے ہی اپاچی کلک ہاؤس استعمال کرتا ہے، لہذا میں نے اس تجزیاتی DBMS پر اپنی تحقیق کو جانچنے کا فیصلہ کیا۔

ایک سادہ ترکیب کا استعمال کرتے ہوئے کلک ہاؤس کو تعینات کیا گیا:

sudo docker run -d --name clickhouse_1 
     --ulimit nofile=262144:262144 
     -v /opt/clickhouse/log:/var/log/clickhouse-server 
     -v /opt/clickhouse/data:/var/lib/clickhouse 
     yandex/clickhouse-server

میں نے اس میں ایک ڈیٹا بیس اور ایک ٹیبل اس طرح بنایا:

CREATE TABLE 
db.steps (`area` Int64, `when` DateTime64(1, 'Europe/Moscow') DEFAULT now64(), `zone` Int64, `person` Int64) 
ENGINE = MergeTree() ORDER BY (area, zone, person) SETTINGS index_granularity = 8192

میں نے اسے درج ذیل اسکرپٹ کا استعمال کرتے ہوئے ڈیٹا سے بھرا ہے۔

from time import time

from clickhouse_driver import Client
from random import random

client = Client('vm-12c2c34c-df68-4a98-b1e5-a4d1cef1acff.domain',
                database='db',
                password='secret')

max = 20

for r in range(0, 100000):

    if r % 1000 == 0:
        print("CNT: {}, TS: {}".format(r, time()))

    data = [{
            'area': 0,
            'zone': 0,
            'person': r
        }]

    while True:
        if random() < 0.3:
            break

        data.append({
                'area': 0,
                'zone': int(random() * (max - 2)) + 1,
                'person': r
            })

    data.append({
            'area': 0,
            'zone': max - 1,
            'person': r
        })

    client.execute(
        'INSERT INTO steps (area, zone, person) VALUES',
        data
    )

چونکہ داخلے بیچوں میں آتے ہیں، بھرنا JanusGraph کے مقابلے میں بہت تیز تھا۔

JOIN کا استعمال کرتے ہوئے دو سوالات بنائے۔ پوائنٹ A سے پوائنٹ B تک جانے کے لیے:

SELECT s1.person AS person,
       s1.zone,
       s1.when,
       s2.zone,
       s2.when
FROM
  (SELECT *
   FROM steps
   WHERE (area = 0)
     AND (zone = 0)) AS s1 ANY INNER JOIN
  (SELECT *
   FROM steps AS s2
   WHERE (area = 0)
     AND (zone = 19)) AS s2 USING person
WHERE s1.when <= s2.when

3 پوائنٹس سے گزرنے کے لیے:

SELECT s3.person,
       s1z,
       s1w,
       s2z,
       s2w,
       s3.zone,
       s3.when
FROM
  (SELECT s1.person AS person,
          s1.zone AS s1z,
          s1.when AS s1w,
          s2.zone AS s2z,
          s2.when AS s2w
   FROM
     (SELECT *
      FROM steps
      WHERE (area = 0)
        AND (zone = 0)) AS s1 ANY INNER JOIN
     (SELECT *
      FROM steps AS s2
      WHERE (area = 0)
        AND (zone = 3)) AS s2 USING person
   WHERE s1.when <= s2.when) p ANY INNER JOIN
  (SELECT *
   FROM steps
   WHERE (area = 0)
     AND (zone = 19)) AS s3 USING person
WHERE p.s2w <= s3.when

درخواستیں، بلاشبہ، کافی خوفناک نظر آتی ہیں؛ حقیقی استعمال کے لیے، آپ کو ایک سافٹ ویئر جنریٹر استعمال کرنے کی ضرورت ہے۔ تاہم، وہ کام کرتے ہیں اور وہ تیزی سے کام کرتے ہیں. پہلی اور دوسری درخواستیں 0.1 سیکنڈ سے بھی کم وقت میں مکمل ہو جاتی ہیں۔ یہاں 3 پوائنٹس سے گزرنے والے شمار (*) کے لیے استفسار کے عمل کے وقت کی ایک مثال ہے۔

SELECT count(*)
FROM 
(
    SELECT 
        s1.person AS person, 
        s1.zone AS s1z, 
        s1.when AS s1w, 
        s2.zone AS s2z, 
        s2.when AS s2w
    FROM 
    (
        SELECT *
        FROM steps
        WHERE (area = 0) AND (zone = 0)
    ) AS s1
    ANY INNER JOIN 
    (
        SELECT *
        FROM steps AS s2
        WHERE (area = 0) AND (zone = 3)
    ) AS s2 USING (person)
    WHERE s1.when <= s2.when
) AS p
ANY INNER JOIN 
(
    SELECT *
    FROM steps
    WHERE (area = 0) AND (zone = 19)
) AS s3 USING (person)
WHERE p.s2w <= s3.when

┌─count()─┐
│   11592 │
└─────────┘

1 rows in set. Elapsed: 0.068 sec. Processed 250.03 thousand rows, 8.00 MB (3.69 million rows/s., 117.98 MB/s.)

IOPS کے بارے میں ایک نوٹ. ڈیٹا کو آباد کرتے وقت، JanusGraph نے کافی زیادہ تعداد میں IOPS (1000-1300 ڈیٹا کی آبادی کے چار دھاگوں کے لیے) پیدا کیا اور IOWAIT کافی زیادہ تھا۔ اسی وقت، ClickHouse نے ڈسک کے سب سسٹم پر کم سے کم بوجھ پیدا کیا۔

حاصل يہ ہوا

ہم نے اس قسم کی درخواست کی خدمت کے لیے ClickHouse استعمال کرنے کا فیصلہ کیا۔ ہم سوالات کو کلک ہاؤس میں لوڈ کرنے سے پہلے اپاچی فلنک کا استعمال کرتے ہوئے ایونٹ سٹریم کو پہلے سے پروسیس کر کے مادی خیالات اور متوازی کا استعمال کرتے ہوئے ہمیشہ مزید بہتر بنا سکتے ہیں۔

کارکردگی اتنی اچھی ہے کہ شاید ہمیں پروگرام کے لحاظ سے پیوٹنگ ٹیبلز کے بارے میں سوچنا بھی نہیں پڑے گا۔ اس سے پہلے، ہمیں اپاچی پارکیٹ پر اپ لوڈ کے ذریعے ورٹیکا سے حاصل کردہ ڈیٹا کے محور بنانے ہوتے تھے۔

بدقسمتی سے، گراف DBMS استعمال کرنے کی ایک اور کوشش ناکام رہی۔ میں نے JanusGraph کو ایسا دوستانہ ماحولیاتی نظام نہیں پایا جس کی وجہ سے پروڈکٹ کے ساتھ رفتار حاصل کرنا آسان ہو گیا۔ اسی وقت، سرور کو ترتیب دینے کے لیے، روایتی جاوا طریقہ استعمال کیا جاتا ہے، جو جاوا سے ناواقف لوگوں کو خون کے آنسو رونے پر مجبور کر دے گا:

host: 0.0.0.0
port: 8182
threadPoolWorker: 1
gremlinPool: 8
scriptEvaluationTimeout: 30000
channelizer: org.janusgraph.channelizers.JanusGraphWsAndHttpChannelizer

graphManager: org.janusgraph.graphdb.management.JanusGraphManager
graphs: {
  ConfigurationManagementGraph: conf/janusgraph-cql-configurationgraph.properties,
  airlines: conf/airlines.properties
}

scriptEngines: {
  gremlin-groovy: {
    plugins: { org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
               org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/airline-sample.groovy]}}}}

serializers:
# GraphBinary is here to replace Gryo and Graphson
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}
  # Gryo and Graphson, latest versions
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  # Older serialization versions for backwards compatibility:
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { serializeResultToString: true }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: {ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}

processors:
  - { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}
  - { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, config: { cacheExpirationTime: 600000, cacheMaxSize: 1000 }}

metrics: {
  consoleReporter: {enabled: false, interval: 180000},
  csvReporter: {enabled: false, interval: 180000, fileName: /tmp/gremlin-server-metrics.csv},
  jmxReporter: {enabled: false},
  slf4jReporter: {enabled: true, interval: 180000},
  gangliaReporter: {enabled: false, interval: 180000, addressingMode: MULTICAST},
  graphiteReporter: {enabled: false, interval: 180000}}
threadPoolBoss: 1
maxInitialLineLength: 4096
maxHeaderSize: 8192
maxChunkSize: 8192
maxContentLength: 65536
maxAccumulationBufferComponents: 1024
resultIterationBatchSize: 64
writeBufferHighWaterMark: 32768
writeBufferHighWaterMark: 65536
ssl: {
  enabled: false}

میں حادثاتی طور پر JanusGraph کے BerkeleyDB ورژن کو "ڈالنے" میں کامیاب ہوگیا۔

دستاویزات اشاریہ جات کے لحاظ سے کافی ٹیڑھی ہیں، کیونکہ اشاریہ جات کا انتظام کرنے کے لیے آپ کو گرووی میں کچھ عجیب و غریب شامانیت انجام دینے کی ضرورت ہوتی ہے۔ مثال کے طور پر، ایک انڈیکس بنانے کے لیے گریملن کنسول میں کوڈ لکھ کر کیا جانا چاہیے (جو، ویسے، باکس سے باہر کام نہیں کرتا)۔ باضابطہ JanusGraph دستاویزات سے:

graph.tx().rollback() //Never create new indexes while a transaction is active
mgmt = graph.openManagement()
name = mgmt.getPropertyKey('name')
age = mgmt.getPropertyKey('age')
mgmt.buildIndex('byNameComposite', Vertex.class).addKey(name).buildCompositeIndex()
mgmt.buildIndex('byNameAndAgeComposite', Vertex.class).addKey(name).addKey(age).buildCompositeIndex()
mgmt.commit()

//Wait for the index to become available
ManagementSystem.awaitGraphIndexStatus(graph, 'byNameComposite').call()
ManagementSystem.awaitGraphIndexStatus(graph, 'byNameAndAgeComposite').call()
//Reindex the existing data
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("byNameComposite"), SchemaAction.REINDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex("byNameAndAgeComposite"), SchemaAction.REINDEX).get()
mgmt.commit()

بعد میں

ایک لحاظ سے، اوپر کا تجربہ گرم اور نرم کے درمیان موازنہ ہے۔ اگر آپ اس کے بارے میں سوچتے ہیں تو، ایک گراف DBMS اسی نتائج حاصل کرنے کے لیے دوسرے آپریشن کرتا ہے۔ تاہم، ٹیسٹوں کے حصے کے طور پر، میں نے ایک درخواست کے ساتھ ایک تجربہ بھی کیا جیسے:

g.V().hasLabel('ZoneStep').has('id',0)
    .repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',1)).count().next()

جو پیدل فاصلے کی عکاسی کرتا ہے۔ تاہم، ایسے اعداد و شمار پر بھی، گراف DBMS نے ایسے نتائج دکھائے جو چند سیکنڈز سے آگے نکل گئے... یہ، یقیناً، اس حقیقت کی وجہ سے ہے کہ اس طرح کے راستے تھے۔ 0 -> X -> Y ... -> 1، جسے گراف انجن نے بھی چیک کیا۔

یہاں تک کہ ایک سوال کے لئے جیسے:

g.V().hasLabel('ZoneStep').has('id',0).out().has('id',1)).count().next()

میں ایک سیکنڈ سے بھی کم وقت کے پروسیسنگ وقت کے ساتھ نتیجہ خیز جواب حاصل کرنے سے قاصر تھا۔

کہانی کا اخلاق یہ ہے کہ ایک خوبصورت خیال اور پیراڈیمٹک ماڈلنگ مطلوبہ نتیجہ کی طرف نہیں لے جاتی، جسے کلک ہاؤس کی مثال کے ساتھ بہت زیادہ کارکردگی کے ساتھ دکھایا جاتا ہے۔ اس مضمون میں پیش کردہ استعمال کیس گراف DBMSs کے لیے ایک واضح مخالف پیٹرن ہے، حالانکہ یہ ان کی تمثیل میں ماڈلنگ کے لیے موزوں معلوم ہوتا ہے۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں