Node.js 同步和x27的正确设置;使用RxJS使用可观测数据对大型数据集(系统到系统)进行数据挖掘

Node.js 同步和x27的正确设置;使用RxJS使用可观测数据对大型数据集(系统到系统)进行数据挖掘,node.js,rxjs,Node.js,Rxjs,我正在尝试使用NodeJS应用程序将数据从一个系统同步到另一个系统,以帮助以事务方式跟踪同步过程。下面是我试图完成的一个修改后的片段。间隔时间和抽头仅用于记录和测试目的。到目前为止,它正在做我想做的事情,但感觉不对或似乎不对 我正在尝试做的事情的总体示意图: 1.注意:最左边的项目是kick starter操作(UI/最终用户操作&调用getActiveCourses的内部间隔) *编辑:我的主要问题是,如何使用RxJS Observables创建一个类似队列管理状态的对象,以允许单个项目在完成

我正在尝试使用NodeJS应用程序将数据从一个系统同步到另一个系统,以帮助以事务方式跟踪同步过程。下面是我试图完成的一个修改后的片段。间隔时间和抽头仅用于记录和测试目的。到目前为止,它正在做我想做的事情,但感觉不对或似乎不对

我正在尝试做的事情的总体示意图: 1.注意:最左边的项目是kick starter操作(UI/最终用户操作&调用getActiveCourses的内部间隔)

*编辑:我的主要问题是,如何使用RxJS Observables创建一个类似队列管理状态的对象,以允许单个项目在完成后进行自我同步和自我处置

*编辑2:我不是在寻求关于设计整个系统的帮助,而是在如何正确地设置一个可以管理课程列表的Observable,然后使用另一个Observable监视器来过滤特定课程方面。在这些过滤的课程上,每个课程都应该是自己的可观察到的,以同步与自身相关的数据,然后自行取消订阅

  • 主要的getActiveCourses应该是行为主题还是主题? 保留当前值或仅在检索最新组时推送 当然
  • processCourses应该是一个主题吗?我的想法是,这将是对filteredCourses的最新订阅的捕获,并像队列一样处理它们。我很难判断这是不是真的
  • 最后,我是否需要创建第三个可观察对象来真正捕获和隔离要同步的实际课程?
  • 有人向我指出,点击并订阅而不是订阅不是执行此任务的可靠方式。一个建议是使用concatMap(),但我发现它仍然是线性的,我更希望数据在流动中更加有机,因为有些数据可能需要比其他数据更多的更新

    请注意,大部分应用程序已经完成,我唯一真正想做的就是高效地编写代码


    因此,在研究了扁平化策略之后,我能够为我所面临的问题创建一个有效的解决方案。总的问题是,我订阅的是一个在一个可观察范围内的一个可观察范围。这基本上是一个合并地图策略,而不是我想要的。事实证明,我需要执行一个switchMap策略,同时使用scan操作符根据当前/累积的数据检查传入的数据

    这篇文章真正帮助我了解了发生的事情:


    我想你需要把你的问题缩小一点。我很乐意帮助你使用RxJS,但是你要求设计一个完整的系统。。。你能把你的问题缩小到处理管道吗?
    const courseProcessQueue = new BehaviorSubject({});
    const processQueuedCourses = new Subject();
    
    processQueuedCourses
    .subscribe((data: any) => // problematic area!!!!!!
      data.pipe(
        filter((d: any) => d.length <= 2),
        tap(loadCourseEnrollments),// end of problematic area
      )
      .subscribe(d => console.log('[processQueuedCourses]', d))
    );
    processQueuedCourses.next(courseProcessQueue);
    
    interval(500)
    .pipe(
      map(loadActiveCourses),
      flatMap(data => data),
      map(courseSyncQueue),
    )
    .subscribe(() => console.log('pushed new course queue state'));
    
    courseProcessQueue.subscribe((courses: any) => 
        console.log('[courseProcessQueue]', courses.length)
    );
    
    function loadActiveCourses() {
      let limit = Math.floor(Math.random() * (10 - 1 + 1) + 1)
      return from(getActiveCourses('externalId id', limit));
    }
    
    function courseSyncQueue(courses: any) {
      courseProcessQueue.next(courses);
    }
    
    async function loadCourseEnrollments(courses: any) {
      console.log('PROCESSING ENROLLMENTS!!!!!!!!')
      courses.map(course => console.log('PROCESSED:', course.externalId));
    }
    
    [getActiveCourses()]
    PROCESSING ENROLLMENTS!!!!!!!!
    PROCESSED: Course-001
    PROCESSED: Course-002
    [ processQueuedCourses] [ { id: '1',
        externalId: 'Course-001' },
      { id: '2',
        externalId: 'Course-002' } ]
    [courseProcessQueue] 2
    pushed new course queue state
    [getActiveCourses()]
    [courseProcessQueue] 8
    pushed new course queue state
    [courseProcessQueue] 9
    pushed new course queue state
    
    import * as mongoose from 'mongoose';
    import * as bluebird from 'bluebird';
    import { interval, from, BehaviorSubject, timer } from 'rxjs';
    import { map, switchMap, scan } from 'rxjs/operators';
    import { mongooseConfig } from './config';
    import { getActiveCourses, getCourseById } from './logic/data-service';
    import { Course } from './models/course';
    // import { inspect } from 'util';
    
    (<any>mongoose).Promise = bluebird;
    
    mongoose.connection.once('open', () => {
      mongoose.connection.on('error', (err: any) => { console.log(err); });
    });
    
    mongoose.connect(mongooseConfig.database, mongooseConfig.options);
    /**
     * Test for updates to a course
     */
    Course.findOneAndUpdate(
      {id: '_69107_1'},
      {'availability.available': 'N'},
      {upsert: true}
    )
    .select('-_id id externalId updatedAt')
    .exec()
    .then(console.log);
    Course.findOneAndUpdate(
      {id: '_69107_1'},
      {'availability.available': 'Y'},
      {upsert: true}
    )
    .select('-_id id externalId updatedAt')
    .exec()
    .then(console.log);
    
    /**
     * END TEST
     */
    
    
    /**
     * getActiveCoursesFromDB => from() => switchMap() => BehaviorSubject()
     */
    
    // just use 3 seconds for now, update to 15 * (60 * 1000) later
    const refresh15MinutesAgo = 3000;
    const filteredCourses = new BehaviorSubject([]);
    
    // get the active courses that are stale test
    interval(1000).pipe(
      map(() => from(getActiveCourses({select: '-_id id externalId updatedAt', limit: 3}))), 
      switchMap(courses => courses),
      map(courses => courses.filter(course => course.updatedAt < Date.now() - refresh15MinutesAgo))
    ).subscribe(setFilteredCourses);
    
    filteredCourses.pipe(
      scan((acc, curr) => {
        // ensure there is a current obj
        if (curr) {
          // if the current obj is not an array then check it directly
          if (!Array.isArray(curr)) {
            if (!containsObject(curr, acc)) {
              return [...new Set([...acc, curr])];
            }
          } else {
            // otherwise process only the ones that are not in the current stack
            return [...new Set([...acc, ...curr.filter(c => !containsObject(c, acc))])];
          }
        }
    
        // either first iteration or curr was undefined
        return acc;
      })
    ).subscribe(logOut);
    
    //test to inject a course and update, simulating a front end override call
    timer(6000).pipe(
      map(() => from(getCourseById('4', {select: '-_id id externalId updatedAt'}))),
      switchMap(c => c)
    ).subscribe(setFilteredCourses);
    
    // helper function to push data into the BehaviorSubject
    function setFilteredCourses(data) {
      filteredCourses.next(data);
    }
    
    function logOut(output: any) {
      console.log('[logOut]', output);
    }
    
    // helper function to test if an object is in our list
    function containsObject(obj, list) {
      const ids = list.filter(c => c.id === obj.id);
      if (ids.length > 0) {
        return true;
      }
      return false;
    }  
    
    //creating an update for test
    { id: '3',
      externalId: 'Course-003',
      updatedAt: 2018-09-11T02:00:39.986Z }
    { id: '3',
      externalId: 'Course-003',
      updatedAt: 2018-09-11T02:01:31.710Z }
    
    //time passes
    [getActiveCourses()]
    [logOut] [ { id: '1',
        externalId: 'Course-001',
        updatedAt: 2018-09-07T16:45:58.295Z },
      { id: '2',
        externalId: 'Course-002',
        updatedAt: 2018-09-07T16:45:58.295Z } ]
    [logOut] [ { id: '1',
        externalId: 'Course-001',
        updatedAt: 2018-09-07T16:45:58.295Z },
      { id: '2',
        externalId: 'Course-002',
        updatedAt: 2018-09-07T16:45:58.295Z },
      { id: '3',
        externalId: 'Course-003',
        updatedAt: 2018-09-11T02:01:31.710Z } ]
    
    //more time passes, 4 was injected into the stack/queue
    [getActiveCourses()]
    [logOut] [ { id: '1',
        externalId: 'Course-001',
        updatedAt: 2018-09-07T16:45:58.295Z },
      { id: '2',
        externalId: 'Course-002',
        updatedAt: 2018-09-07T16:45:58.295Z },
      { id: '3',
        externalId: 'Course-003',
        updatedAt: 2018-09-11T02:01:31.710Z },
      { id: '4',
        externalId: 'Course-004',
        updatedAt: 2018-09-07T16:45:58.295Z } ]